+static void
+facet_account(struct facet *facet)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ struct subfacet *subfacet;
+ const struct nlattr *a;
+ unsigned int left;
+ ovs_be16 vlan_tci;
+ uint64_t n_bytes;
+
+ if (!facet->has_normal || !ofproto->has_bonded_bundles) {
+ return;
+ }
+ n_bytes = facet->byte_count - facet->accounted_bytes;
+
+ /* This loop feeds byte counters to bond_account() for rebalancing to use
+ * as a basis. We also need to track the actual VLAN on which the packet
+ * is going to be sent to ensure that it matches the one passed to
+ * bond_choose_output_slave(). (Otherwise, we will account to the wrong
+ * hash bucket.)
+ *
+ * We use the actions from an arbitrary subfacet because they should all
+ * be equally valid for our purpose. */
+ subfacet = CONTAINER_OF(list_front(&facet->subfacets),
+ struct subfacet, list_node);
+ vlan_tci = facet->flow.vlan_tci;
+ NL_ATTR_FOR_EACH_UNSAFE (a, left,
+ subfacet->actions, subfacet->actions_len) {
+ const struct ovs_action_push_vlan *vlan;
+ struct ofport_dpif *port;
+
+ switch (nl_attr_type(a)) {
+ case OVS_ACTION_ATTR_OUTPUT:
+ port = get_odp_port(ofproto, nl_attr_get_u32(a));
+ if (port && port->bundle && port->bundle->bond) {
+ bond_account(port->bundle->bond, &facet->flow,
+ vlan_tci_to_vid(vlan_tci), n_bytes);
+ }
+ break;
+
+ case OVS_ACTION_ATTR_POP_VLAN:
+ vlan_tci = htons(0);
+ break;
+
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ vlan = nl_attr_get(a);
+ vlan_tci = vlan->vlan_tci;
+ break;
+ }
+ }
+}
+
+/* Returns true if the only action for 'facet' is to send to the controller.
+ * (We don't report NetFlow expiration messages for such facets because they
+ * are just part of the control logic for the network, not real traffic). */
+static bool
+facet_is_controller_flow(struct facet *facet)
+{
+ if (facet) {
+ const struct rule *rule = &facet->rule->up;
+ const struct ofpact *ofpacts = rule->ofpacts;
+ size_t ofpacts_len = rule->ofpacts_len;
+
+ if (ofpacts_len > 0 &&
+ ofpacts->type == OFPACT_CONTROLLER &&
+ ofpact_next(ofpacts) >= ofpact_end(ofpacts, ofpacts_len)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Folds all of 'facet''s statistics into its rule. Also updates the
+ * accounting ofhook and emits a NetFlow expiration if appropriate. All of
+ * 'facet''s statistics in the datapath should have been zeroed and folded into
+ * its packet and byte counts before this function is called. */
+static void
+facet_flush_stats(struct facet *facet)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ struct subfacet *subfacet;
+
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ assert(!subfacet->dp_byte_count);
+ assert(!subfacet->dp_packet_count);
+ }
+
+ facet_push_stats(facet);
+ if (facet->accounted_bytes < facet->byte_count) {
+ facet_account(facet);
+ facet->accounted_bytes = facet->byte_count;
+ }
+
+ if (ofproto->netflow && !facet_is_controller_flow(facet)) {
+ struct ofexpired expired;
+ expired.flow = facet->flow;
+ expired.packet_count = facet->packet_count;
+ expired.byte_count = facet->byte_count;
+ expired.used = facet->used;
+ netflow_expire(ofproto->netflow, &facet->nf_flow, &expired);
+ }
+
+ facet->rule->packet_count += facet->packet_count;
+ facet->rule->byte_count += facet->byte_count;
+
+ /* Reset counters to prevent double counting if 'facet' ever gets
+ * reinstalled. */
+ facet_reset_counters(facet);
+
+ netflow_flow_clear(&facet->nf_flow);
+ facet->tcp_flags = 0;
+}
+
+/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
+ * Returns it if found, otherwise a null pointer.
+ *
+ * 'hash' must be the return value of flow_hash(flow, 0).
+ *
+ * The returned facet might need revalidation; use facet_lookup_valid()
+ * instead if that is important. */
+static struct facet *
+facet_find(struct ofproto_dpif *ofproto,
+ const struct flow *flow, uint32_t hash)
+{
+ struct facet *facet;
+
+ HMAP_FOR_EACH_WITH_HASH (facet, hmap_node, hash, &ofproto->facets) {
+ if (flow_equal(flow, &facet->flow)) {
+ return facet;
+ }
+ }
+
+ return NULL;
+}
+
+/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
+ * Returns it if found, otherwise a null pointer.
+ *
+ * 'hash' must be the return value of flow_hash(flow, 0).
+ *
+ * The returned facet is guaranteed to be valid. */
+static struct facet *
+facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow,
+ uint32_t hash)
+{
+ struct facet *facet;
+
+ facet = facet_find(ofproto, flow, hash);
+ if (facet
+ && (ofproto->need_revalidate
+ || tag_set_intersects(&ofproto->revalidate_set, facet->tags))) {
+ facet_revalidate(facet);
+ }
+
+ return facet;
+}
+
+static const char *
+subfacet_path_to_string(enum subfacet_path path)
+{
+ switch (path) {
+ case SF_NOT_INSTALLED:
+ return "not installed";
+ case SF_FAST_PATH:
+ return "in fast path";
+ case SF_SLOW_PATH:
+ return "in slow path";
+ default:
+ return "<error>";
+ }
+}
+
+/* Returns the path in which a subfacet should be installed if its 'slow'
+ * member has the specified value. */
+static enum subfacet_path
+subfacet_want_path(enum slow_path_reason slow)
+{
+ return slow ? SF_SLOW_PATH : SF_FAST_PATH;
+}
+
+/* Returns true if 'subfacet' needs to have its datapath flow updated,
+ * supposing that its actions have been recalculated as 'want_actions' and that
+ * 'slow' is nonzero iff 'subfacet' should be in the slow path. */
+static bool
+subfacet_should_install(struct subfacet *subfacet, enum slow_path_reason slow,
+ const struct ofpbuf *want_actions)
+{
+ enum subfacet_path want_path = subfacet_want_path(slow);
+ return (want_path != subfacet->path
+ || (want_path == SF_FAST_PATH
+ && (subfacet->actions_len != want_actions->size
+ || memcmp(subfacet->actions, want_actions->data,
+ subfacet->actions_len))));
+}
+
+static bool
+facet_check_consistency(struct facet *facet)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 15);
+
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+
+ uint64_t odp_actions_stub[1024 / 8];
+ struct ofpbuf odp_actions;
+
+ struct rule_dpif *rule;
+ struct subfacet *subfacet;
+ bool may_log = false;
+ bool ok;
+
+ /* Check the rule for consistency. */
+ rule = rule_dpif_lookup(ofproto, &facet->flow);
+ ok = rule == facet->rule;
+ if (!ok) {
+ may_log = !VLOG_DROP_WARN(&rl);
+ if (may_log) {
+ struct ds s;
+
+ ds_init(&s);
+ flow_format(&s, &facet->flow);
+ ds_put_format(&s, ": facet associated with wrong rule (was "
+ "table=%"PRIu8",", facet->rule->up.table_id);
+ cls_rule_format(&facet->rule->up.cr, &s);
+ ds_put_format(&s, ") (should have been table=%"PRIu8",",
+ rule->up.table_id);
+ cls_rule_format(&rule->up.cr, &s);
+ ds_put_char(&s, ')');
+
+ VLOG_WARN("%s", ds_cstr(&s));
+ ds_destroy(&s);
+ }
+ }
+
+ /* Check the datapath actions for consistency. */
+ ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ enum subfacet_path want_path;
+ struct odputil_keybuf keybuf;
+ struct action_xlate_ctx ctx;
+ struct ofpbuf key;
+ struct ds s;
+
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
+ subfacet->initial_tci, rule, 0, NULL);
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
+ &odp_actions);
+
+ if (subfacet->path == SF_NOT_INSTALLED) {
+ /* This only happens if the datapath reported an error when we
+ * tried to install the flow. Don't flag another error here. */
+ continue;
+ }
+
+ want_path = subfacet_want_path(subfacet->slow);
+ if (want_path == SF_SLOW_PATH && subfacet->path == SF_SLOW_PATH) {
+ /* The actions for slow-path flows may legitimately vary from one
+ * packet to the next. We're done. */
+ continue;
+ }
+
+ if (!subfacet_should_install(subfacet, subfacet->slow, &odp_actions)) {
+ continue;
+ }
+
+ /* Inconsistency! */
+ if (ok) {
+ may_log = !VLOG_DROP_WARN(&rl);
+ ok = false;
+ }
+ if (!may_log) {
+ /* Rate-limited, skip reporting. */
+ continue;
+ }
+
+ ds_init(&s);
+ subfacet_get_key(subfacet, &keybuf, &key);
+ odp_flow_key_format(key.data, key.size, &s);
+
+ ds_put_cstr(&s, ": inconsistency in subfacet");
+ if (want_path != subfacet->path) {
+ enum odp_key_fitness fitness = subfacet->key_fitness;
+
+ ds_put_format(&s, " (%s, fitness=%s)",
+ subfacet_path_to_string(subfacet->path),
+ odp_key_fitness_to_string(fitness));
+ ds_put_format(&s, " (should have been %s)",
+ subfacet_path_to_string(want_path));
+ } else if (want_path == SF_FAST_PATH) {
+ ds_put_cstr(&s, " (actions were: ");
+ format_odp_actions(&s, subfacet->actions,
+ subfacet->actions_len);
+ ds_put_cstr(&s, ") (correct actions: ");
+ format_odp_actions(&s, odp_actions.data, odp_actions.size);
+ ds_put_char(&s, ')');
+ } else {
+ ds_put_cstr(&s, " (actions: ");
+ format_odp_actions(&s, subfacet->actions,
+ subfacet->actions_len);
+ ds_put_char(&s, ')');
+ }
+ VLOG_WARN("%s", ds_cstr(&s));
+ ds_destroy(&s);
+ }
+ ofpbuf_uninit(&odp_actions);
+
+ return ok;
+}
+
+/* Re-searches the classifier for 'facet':
+ *
+ * - If the rule found is different from 'facet''s current rule, moves
+ * 'facet' to the new rule and recompiles its actions.
+ *
+ * - If the rule found is the same as 'facet''s current rule, leaves 'facet'
+ * where it is and recompiles its actions anyway. */
+static void
+facet_revalidate(struct facet *facet)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ struct actions {
+ struct nlattr *odp_actions;
+ size_t actions_len;
+ };
+ struct actions *new_actions;
+
+ struct action_xlate_ctx ctx;
+ uint64_t odp_actions_stub[1024 / 8];
+ struct ofpbuf odp_actions;
+
+ struct rule_dpif *new_rule;
+ struct subfacet *subfacet;
+ int i;
+
+ COVERAGE_INC(facet_revalidate);
+
+ new_rule = rule_dpif_lookup(ofproto, &facet->flow);
+
+ /* Calculate new datapath actions.
+ *
+ * We do not modify any 'facet' state yet, because we might need to, e.g.,
+ * emit a NetFlow expiration and, if so, we need to have the old state
+ * around to properly compose it. */
+
+ /* If the datapath actions changed or the installability changed,
+ * then we need to talk to the datapath. */
+ i = 0;
+ new_actions = NULL;
+ memset(&ctx, 0, sizeof ctx);
+ ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ enum slow_path_reason slow;
+
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
+ subfacet->initial_tci, new_rule, 0, NULL);
+ xlate_actions(&ctx, new_rule->up.ofpacts, new_rule->up.ofpacts_len,
+ &odp_actions);
+
+ slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
+ if (subfacet_should_install(subfacet, slow, &odp_actions)) {
+ struct dpif_flow_stats stats;
+
+ subfacet_install(subfacet,
+ odp_actions.data, odp_actions.size, &stats, slow);
+ subfacet_update_stats(subfacet, &stats);
+
+ if (!new_actions) {
+ new_actions = xcalloc(list_size(&facet->subfacets),
+ sizeof *new_actions);
+ }
+ new_actions[i].odp_actions = xmemdup(odp_actions.data,
+ odp_actions.size);
+ new_actions[i].actions_len = odp_actions.size;
+ }
+
+ i++;
+ }
+ ofpbuf_uninit(&odp_actions);
+
+ if (new_actions) {
+ facet_flush_stats(facet);
+ }
+
+ /* Update 'facet' now that we've taken care of all the old state. */
+ facet->tags = ctx.tags;
+ facet->nf_flow.output_iface = ctx.nf_output_iface;
+ facet->has_learn = ctx.has_learn;
+ facet->has_normal = ctx.has_normal;
+ facet->has_fin_timeout = ctx.has_fin_timeout;
+ facet->mirrors = ctx.mirrors;
+
+ i = 0;
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ subfacet->slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
+
+ if (new_actions && new_actions[i].odp_actions) {
+ free(subfacet->actions);
+ subfacet->actions = new_actions[i].odp_actions;
+ subfacet->actions_len = new_actions[i].actions_len;
+ }
+ i++;
+ }
+ free(new_actions);
+
+ if (facet->rule != new_rule) {
+ COVERAGE_INC(facet_changed_rule);
+ list_remove(&facet->list_node);
+ list_push_back(&new_rule->facets, &facet->list_node);
+ facet->rule = new_rule;
+ facet->used = new_rule->up.created;
+ facet->prev_used = facet->used;
+ }
+}
+
+/* Updates 'facet''s used time. Caller is responsible for calling
+ * facet_push_stats() to update the flows which 'facet' resubmits into. */
+static void
+facet_update_time(struct facet *facet, long long int used)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ if (used > facet->used) {
+ facet->used = used;
+ ofproto_rule_update_used(&facet->rule->up, used);
+ netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, used);
+ }
+}
+
+static void
+facet_reset_counters(struct facet *facet)
+{
+ facet->packet_count = 0;
+ facet->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)
+{
+ struct dpif_flow_stats stats;
+
+ assert(facet->packet_count >= facet->prev_packet_count);
+ assert(facet->byte_count >= facet->prev_byte_count);
+ assert(facet->used >= facet->prev_used);
+
+ stats.n_packets = facet->packet_count - facet->prev_packet_count;
+ stats.n_bytes = facet->byte_count - facet->prev_byte_count;
+ stats.used = facet->used;
+ stats.tcp_flags = 0;
+
+ if (stats.n_packets || stats.n_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, &stats);
+
+ update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto),
+ facet->mirrors, stats.n_packets, stats.n_bytes);
+ }
+}
+
+static void
+rule_credit_stats(struct rule_dpif *rule, const struct dpif_flow_stats *stats)
+{
+ rule->packet_count += stats->n_packets;
+ rule->byte_count += stats->n_bytes;
+ ofproto_rule_update_used(&rule->up, stats->used);
+}
+
+/* Pushes flow statistics to the rules which 'flow' resubmits into given
+ * 'rule''s actions and mirrors. */
+static void
+flow_push_stats(struct rule_dpif *rule,
+ const struct flow *flow, const struct dpif_flow_stats *stats)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct action_xlate_ctx ctx;
+
+ ofproto_rule_update_used(&rule->up, stats->used);
+
+ action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule,
+ 0, NULL);
+ ctx.resubmit_stats = stats;
+ xlate_actions_for_side_effects(&ctx, rule->up.ofpacts,
+ rule->up.ofpacts_len);
+}
+\f
+/* Subfacets. */
+
+static struct subfacet *
+subfacet_find(struct ofproto_dpif *ofproto,
+ const struct nlattr *key, size_t key_len, uint32_t key_hash,
+ const struct flow *flow)
+{
+ struct subfacet *subfacet;
+
+ HMAP_FOR_EACH_WITH_HASH (subfacet, hmap_node, key_hash,
+ &ofproto->subfacets) {
+ if (subfacet->key
+ ? (subfacet->key_len == key_len
+ && !memcmp(key, subfacet->key, key_len))
+ : flow_equal(flow, &subfacet->facet->flow)) {
+ return subfacet;
+ }
+ }
+
+ return NULL;
+}
+
+/* Searches 'facet' (within 'ofproto') for a subfacet with the specified
+ * 'key_fitness', 'key', and 'key_len'. Returns the existing subfacet if
+ * there is one, otherwise creates and returns a new subfacet.
+ *
+ * If the returned subfacet is new, then subfacet->actions will be NULL, in
+ * which case the caller must populate the actions with
+ * subfacet_make_actions(). */
+static struct subfacet *
+subfacet_create(struct facet *facet, enum odp_key_fitness key_fitness,
+ const struct nlattr *key, size_t key_len,
+ ovs_be16 initial_tci, long long int now)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ uint32_t key_hash = odp_flow_key_hash(key, key_len);
+ struct subfacet *subfacet;
+
+ if (list_is_empty(&facet->subfacets)) {
+ subfacet = &facet->one_subfacet;
+ } else {
+ subfacet = subfacet_find(ofproto, key, key_len, key_hash,
+ &facet->flow);
+ if (subfacet) {
+ if (subfacet->facet == facet) {
+ return subfacet;
+ }
+
+ /* This shouldn't happen. */
+ VLOG_ERR_RL(&rl, "subfacet with wrong facet");
+ subfacet_destroy(subfacet);
+ }
+
+ subfacet = xmalloc(sizeof *subfacet);
+ }
+
+ hmap_insert(&ofproto->subfacets, &subfacet->hmap_node, key_hash);
+ list_push_back(&facet->subfacets, &subfacet->list_node);
+ subfacet->facet = facet;
+ subfacet->key_fitness = key_fitness;
+ if (key_fitness != ODP_FIT_PERFECT) {
+ subfacet->key = xmemdup(key, key_len);
+ subfacet->key_len = key_len;
+ } else {
+ subfacet->key = NULL;
+ subfacet->key_len = 0;
+ }
+ subfacet->used = now;
+ subfacet->dp_packet_count = 0;
+ subfacet->dp_byte_count = 0;
+ subfacet->actions_len = 0;
+ subfacet->actions = NULL;
+ subfacet->slow = (subfacet->key_fitness == ODP_FIT_TOO_LITTLE
+ ? SLOW_MATCH
+ : 0);
+ subfacet->path = SF_NOT_INSTALLED;
+ subfacet->initial_tci = initial_tci;
+
+ return subfacet;
+}
+
+/* Uninstalls 'subfacet' from the datapath, if it is installed, removes it from
+ * its facet within 'ofproto', and frees it. */
+static void
+subfacet_destroy__(struct subfacet *subfacet)
+{
+ struct facet *facet = subfacet->facet;
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+
+ subfacet_uninstall(subfacet);
+ hmap_remove(&ofproto->subfacets, &subfacet->hmap_node);
+ list_remove(&subfacet->list_node);
+ free(subfacet->key);
+ free(subfacet->actions);
+ if (subfacet != &facet->one_subfacet) {
+ free(subfacet);
+ }
+}
+
+/* Destroys 'subfacet', as with subfacet_destroy__(), and then if this was the
+ * last remaining subfacet in its facet destroys the facet too. */
+static void
+subfacet_destroy(struct subfacet *subfacet)
+{
+ struct facet *facet = subfacet->facet;
+
+ if (list_is_singleton(&facet->subfacets)) {
+ /* facet_remove() needs at least one subfacet (it will remove it). */
+ facet_remove(facet);
+ } else {
+ subfacet_destroy__(subfacet);
+ }
+}
+
+static void
+subfacet_destroy_batch(struct ofproto_dpif *ofproto,
+ struct subfacet **subfacets, int n)
+{
+ struct odputil_keybuf keybufs[SUBFACET_DESTROY_MAX_BATCH];
+ struct dpif_op ops[SUBFACET_DESTROY_MAX_BATCH];
+ struct dpif_op *opsp[SUBFACET_DESTROY_MAX_BATCH];
+ struct ofpbuf keys[SUBFACET_DESTROY_MAX_BATCH];
+ struct dpif_flow_stats stats[SUBFACET_DESTROY_MAX_BATCH];
+ int i;
+
+ for (i = 0; i < n; i++) {
+ ops[i].type = DPIF_OP_FLOW_DEL;
+ subfacet_get_key(subfacets[i], &keybufs[i], &keys[i]);
+ ops[i].u.flow_del.key = keys[i].data;
+ ops[i].u.flow_del.key_len = keys[i].size;
+ ops[i].u.flow_del.stats = &stats[i];
+ opsp[i] = &ops[i];
+ }
+
+ dpif_operate(ofproto->backer->dpif, opsp, n);
+ for (i = 0; i < n; i++) {
+ subfacet_reset_dp_stats(subfacets[i], &stats[i]);
+ subfacets[i]->path = SF_NOT_INSTALLED;
+ subfacet_destroy(subfacets[i]);
+ }
+}
+
+/* Initializes 'key' with the sequence of OVS_KEY_ATTR_* Netlink attributes
+ * that can be used to refer to 'subfacet'. The caller must provide 'keybuf'
+ * for use as temporary storage. */
+static void
+subfacet_get_key(struct subfacet *subfacet, struct odputil_keybuf *keybuf,
+ struct ofpbuf *key)
+{
+
+ if (!subfacet->key) {
+ struct ofproto_dpif *ofproto;
+ struct flow *flow = &subfacet->facet->flow;
+
+ ofpbuf_use_stack(key, keybuf, sizeof *keybuf);
+ ofproto = ofproto_dpif_cast(subfacet->facet->rule->up.ofproto);
+ odp_flow_key_from_flow(key, flow,
+ ofp_port_to_odp_port(ofproto, flow->in_port));
+ } else {
+ ofpbuf_use_const(key, subfacet->key, subfacet->key_len);
+ }
+}
+
+/* Composes the datapath actions for 'subfacet' based on its rule's actions.
+ * Translates the actions into 'odp_actions', which the caller must have
+ * initialized and is responsible for uninitializing. */
+static void
+subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
+ struct ofpbuf *odp_actions)
+{
+ struct facet *facet = subfacet->facet;
+ struct rule_dpif *rule = facet->rule;
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+
+ struct action_xlate_ctx ctx;
+
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
+ rule, 0, packet);
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, odp_actions);
+ facet->tags = ctx.tags;
+ facet->has_learn = ctx.has_learn;
+ facet->has_normal = ctx.has_normal;
+ facet->has_fin_timeout = ctx.has_fin_timeout;
+ facet->nf_flow.output_iface = ctx.nf_output_iface;
+ facet->mirrors = ctx.mirrors;
+
+ subfacet->slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
+ if (subfacet->actions_len != odp_actions->size
+ || memcmp(subfacet->actions, odp_actions->data, odp_actions->size)) {
+ free(subfacet->actions);
+ subfacet->actions_len = odp_actions->size;
+ subfacet->actions = xmemdup(odp_actions->data, odp_actions->size);
+ }
+}
+
+/* Updates 'subfacet''s datapath flow, setting its actions to 'actions_len'
+ * bytes of actions in 'actions'. If 'stats' is non-null, statistics counters
+ * in the datapath will be zeroed and 'stats' will be updated with traffic new
+ * since 'subfacet' was last updated.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+static int
+subfacet_install(struct subfacet *subfacet,
+ const struct nlattr *actions, size_t actions_len,
+ struct dpif_flow_stats *stats,
+ enum slow_path_reason slow)
+{
+ struct facet *facet = subfacet->facet;
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ enum subfacet_path path = subfacet_want_path(slow);
+ uint64_t slow_path_stub[128 / 8];
+ struct odputil_keybuf keybuf;
+ enum dpif_flow_put_flags flags;
+ struct ofpbuf key;
+ int ret;
+
+ flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
+ if (stats) {
+ flags |= DPIF_FP_ZERO_STATS;
+ }
+
+ if (path == SF_SLOW_PATH) {
+ compose_slow_path(ofproto, &facet->flow, slow,
+ slow_path_stub, sizeof slow_path_stub,
+ &actions, &actions_len);
+ }
+
+ subfacet_get_key(subfacet, &keybuf, &key);
+ ret = dpif_flow_put(ofproto->backer->dpif, flags, key.data, key.size,
+ actions, actions_len, stats);
+
+ if (stats) {
+ subfacet_reset_dp_stats(subfacet, stats);
+ }
+
+ if (!ret) {
+ subfacet->path = path;
+ }
+ return ret;
+}
+
+static int
+subfacet_reinstall(struct subfacet *subfacet, struct dpif_flow_stats *stats)
+{
+ return subfacet_install(subfacet, subfacet->actions, subfacet->actions_len,
+ stats, subfacet->slow);
+}
+
+/* If 'subfacet' is installed in the datapath, uninstalls it. */
+static void
+subfacet_uninstall(struct subfacet *subfacet)
+{
+ if (subfacet->path != SF_NOT_INSTALLED) {
+ struct rule_dpif *rule = subfacet->facet->rule;
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct odputil_keybuf keybuf;
+ struct dpif_flow_stats stats;
+ struct ofpbuf key;
+ int error;
+
+ subfacet_get_key(subfacet, &keybuf, &key);
+ error = dpif_flow_del(ofproto->backer->dpif,
+ key.data, key.size, &stats);
+ subfacet_reset_dp_stats(subfacet, &stats);
+ if (!error) {
+ subfacet_update_stats(subfacet, &stats);
+ }
+ subfacet->path = SF_NOT_INSTALLED;
+ } else {
+ assert(subfacet->dp_packet_count == 0);
+ assert(subfacet->dp_byte_count == 0);
+ }
+}
+
+/* Resets 'subfacet''s datapath statistics counters. This should be called
+ * when 'subfacet''s statistics are cleared in the datapath. If 'stats' is
+ * non-null, it should contain the statistics returned by dpif when 'subfacet'
+ * was reset in the datapath. 'stats' will be modified to include only
+ * statistics new since 'subfacet' was last updated. */
+static void
+subfacet_reset_dp_stats(struct subfacet *subfacet,
+ struct dpif_flow_stats *stats)
+{
+ if (stats
+ && subfacet->dp_packet_count <= stats->n_packets
+ && subfacet->dp_byte_count <= stats->n_bytes) {
+ stats->n_packets -= subfacet->dp_packet_count;
+ stats->n_bytes -= subfacet->dp_byte_count;
+ }
+
+ subfacet->dp_packet_count = 0;
+ subfacet->dp_byte_count = 0;
+}
+
+/* Updates 'subfacet''s used time. The caller is responsible for calling
+ * facet_push_stats() to update the flows which 'subfacet' resubmits into. */
+static void
+subfacet_update_time(struct subfacet *subfacet, long long int used)
+{
+ if (used > subfacet->used) {
+ subfacet->used = used;
+ facet_update_time(subfacet->facet, used);
+ }
+}
+
+/* Folds the statistics from 'stats' into the counters in 'subfacet'.
+ *
+ * Because of the meaning of a subfacet's counters, it only makes sense to do
+ * this if 'stats' are not tracked in the datapath, that is, if 'stats'
+ * represents a packet that was sent by hand or if it represents statistics
+ * that have been cleared out of the datapath. */
+static void
+subfacet_update_stats(struct subfacet *subfacet,
+ const struct dpif_flow_stats *stats)
+{
+ if (stats->n_packets || stats->used > subfacet->used) {
+ struct facet *facet = subfacet->facet;
+
+ subfacet_update_time(subfacet, stats->used);
+ facet->packet_count += stats->n_packets;
+ facet->byte_count += stats->n_bytes;
+ facet->tcp_flags |= stats->tcp_flags;
+ facet_push_stats(facet);
+ netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
+ }
+}
+\f
+/* Rules. */
+
+static struct rule_dpif *
+rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
+{
+ struct rule_dpif *rule;
+
+ rule = rule_dpif_lookup__(ofproto, flow, 0);
+ if (rule) {
+ return rule;
+ }
+
+ return rule_dpif_miss_rule(ofproto, flow);
+}
+
+static struct rule_dpif *
+rule_dpif_lookup__(struct ofproto_dpif *ofproto, const struct flow *flow,
+ uint8_t table_id)
+{
+ struct cls_rule *cls_rule;
+ struct classifier *cls;
+
+ if (table_id >= N_TABLES) {
+ return NULL;
+ }
+
+ cls = &ofproto->up.tables[table_id].cls;
+ if (flow->nw_frag & FLOW_NW_FRAG_ANY
+ && ofproto->up.frag_handling == OFPC_FRAG_NORMAL) {
+ /* For OFPC_NORMAL frag_handling, we must pretend that transport ports
+ * are unavailable. */
+ struct flow ofpc_normal_flow = *flow;
+ ofpc_normal_flow.tp_src = htons(0);
+ ofpc_normal_flow.tp_dst = htons(0);
+ cls_rule = classifier_lookup(cls, &ofpc_normal_flow);
+ } else {
+ cls_rule = classifier_lookup(cls, flow);
+ }
+ return rule_dpif_cast(rule_from_cls_rule(cls_rule));
+}
+
+static struct rule_dpif *
+rule_dpif_miss_rule(struct ofproto_dpif *ofproto, const struct flow *flow)
+{
+ struct ofport_dpif *port;
+
+ port = get_ofp_port(ofproto, flow->in_port);
+ if (!port) {
+ VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, flow->in_port);
+ return ofproto->miss_rule;
+ }
+
+ if (port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN) {
+ return ofproto->no_packet_in_rule;
+ }
+ return ofproto->miss_rule;
+}
+
+static void
+complete_operation(struct rule_dpif *rule)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+
+ rule_invalidate(rule);
+ if (clogged) {
+ struct dpif_completion *c = xmalloc(sizeof *c);
+ c->op = rule->up.pending;
+ list_push_back(&ofproto->completions, &c->list_node);
+ } else {
+ ofoperation_complete(rule->up.pending, 0);
+ }
+}
+
+static struct rule *
+rule_alloc(void)
+{
+ struct rule_dpif *rule = xmalloc(sizeof *rule);
+ return &rule->up;
+}
+
+static void
+rule_dealloc(struct rule *rule_)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ free(rule);
+}
+
+static enum ofperr
+rule_construct(struct rule *rule_)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct rule_dpif *victim;
+ uint8_t table_id;
+
+ rule->packet_count = 0;
+ rule->byte_count = 0;
+
+ victim = rule_dpif_cast(ofoperation_get_victim(rule->up.pending));
+ if (victim && !list_is_empty(&victim->facets)) {
+ struct facet *facet;
+
+ rule->facets = victim->facets;
+ list_moved(&rule->facets);
+ LIST_FOR_EACH (facet, list_node, &rule->facets) {
+ /* XXX: We're only clearing our local counters here. It's possible
+ * that quite a few packets are unaccounted for in the datapath
+ * statistics. These will be accounted to the new rule instead of
+ * cleared as required. This could be fixed by clearing out the
+ * datapath statistics for this facet, but currently it doesn't
+ * seem worth it. */
+ facet_reset_counters(facet);
+ facet->rule = rule;
+ }
+ } else {
+ /* Must avoid list_moved() in this case. */
+ list_init(&rule->facets);
+ }
+
+ table_id = rule->up.table_id;
+ if (victim) {
+ rule->tag = victim->tag;
+ } else if (table_id == 0) {
+ rule->tag = 0;
+ } else {
+ struct flow flow;
+
+ miniflow_expand(&rule->up.cr.match.flow, &flow);
+ rule->tag = rule_calculate_tag(&flow, &rule->up.cr.match.mask,
+ ofproto->tables[table_id].basis);
+ }
+
+ complete_operation(rule);
+ return 0;
+}
+
+static void
+rule_destruct(struct rule *rule_)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ struct facet *facet, *next_facet;
+
+ LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
+ facet_revalidate(facet);
+ }
+
+ complete_operation(rule);
+}
+
+static void
+rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ struct facet *facet;
+
+ /* Start from historical data for 'rule' itself that are no longer tracked
+ * in facets. This counts, for example, facets that have expired. */
+ *packets = rule->packet_count;
+ *bytes = rule->byte_count;
+
+ /* Add any statistics that are tracked by facets. This includes
+ * statistical data recently updated by ofproto_update_stats() as well as
+ * stats for packets that were executed "by hand" via dpif_execute(). */
+ LIST_FOR_EACH (facet, list_node, &rule->facets) {
+ *packets += facet->packet_count;
+ *bytes += facet->byte_count;
+ }
+}
+
+static enum ofperr
+rule_execute(struct rule *rule_, const struct flow *flow,
+ struct ofpbuf *packet)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+
+ struct dpif_flow_stats stats;
+
+ struct action_xlate_ctx ctx;
+ uint64_t odp_actions_stub[1024 / 8];
+ struct ofpbuf odp_actions;
+
+ dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
+ rule_credit_stats(rule, &stats);
+
+ ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
+ action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
+ rule, stats.tcp_flags, packet);
+ ctx.resubmit_stats = &stats;
+ xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions);
+
+ execute_odp_actions(ofproto, flow, odp_actions.data,
+ odp_actions.size, packet);
+
+ ofpbuf_uninit(&odp_actions);
+
+ return 0;
+}
+
+static void
+rule_modify_actions(struct rule *rule_)
+{
+ struct rule_dpif *rule = rule_dpif_cast(rule_);
+
+ complete_operation(rule);
+}
+\f
+/* Sends 'packet' out 'ofport'.
+ * May modify 'packet'.
+ * Returns 0 if successful, otherwise a positive errno value. */
+static int
+send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
+{
+ const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ struct ofpbuf key, odp_actions;
+ struct odputil_keybuf keybuf;
+ uint32_t odp_port;
+ struct flow flow;
+ int error;
+
+ flow_extract(packet, 0, NULL, 0, &flow);
+ odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
+ flow.vlan_tci);
+ if (odp_port != ofport->odp_port) {
+ eth_pop_vlan(packet);
+ flow.vlan_tci = htons(0);
+ }
+
+ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+ odp_flow_key_from_flow(&key, &flow,
+ ofp_port_to_odp_port(ofproto, flow.in_port));
+
+ ofpbuf_init(&odp_actions, 32);
+ compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
+
+ nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
+ error = dpif_execute(ofproto->backer->dpif,
+ key.data, key.size,
+ odp_actions.data, odp_actions.size,
+ packet);
+ ofpbuf_uninit(&odp_actions);
+
+ if (error) {
+ VLOG_WARN_RL(&rl, "%s: failed to send packet on port %"PRIu32" (%s)",
+ ofproto->up.name, odp_port, strerror(error));
+ }
+ ofproto_update_local_port_stats(ofport->up.ofproto, packet->size, 0);
+ return error;
+}
+\f
+/* OpenFlow to datapath action translation. */
+
+static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
+ struct action_xlate_ctx *);
+static void xlate_normal(struct action_xlate_ctx *);
+
+/* Composes an ODP action for a "slow path" action for 'flow' within 'ofproto'.
+ * The action will state 'slow' as the reason that the action is in the slow
+ * path. (This is purely informational: it allows a human viewing "ovs-dpctl
+ * dump-flows" output to see why a flow is in the slow path.)
+ *
+ * The 'stub_size' bytes in 'stub' will be used to store the action.
+ * 'stub_size' must be large enough for the action.
+ *
+ * The action and its size will be stored in '*actionsp' and '*actions_lenp',
+ * respectively. */
+static void
+compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
+ enum slow_path_reason slow,
+ uint64_t *stub, size_t stub_size,
+ const struct nlattr **actionsp, size_t *actions_lenp)
+{
+ union user_action_cookie cookie;
+ struct ofpbuf buf;
+
+ cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
+ cookie.slow_path.unused = 0;
+ cookie.slow_path.reason = slow;
+
+ ofpbuf_use_stack(&buf, stub, stub_size);
+ if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
+ uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT16_MAX);
+ odp_put_userspace_action(pid, &cookie, &buf);
+ } else {
+ put_userspace_action(ofproto, &buf, flow, &cookie);
+ }
+ *actionsp = buf.data;
+ *actions_lenp = buf.size;
+}
+
+static size_t
+put_userspace_action(const struct ofproto_dpif *ofproto,
+ struct ofpbuf *odp_actions,
+ const struct flow *flow,
+ const union user_action_cookie *cookie)
+{
+ uint32_t pid;
+
+ pid = dpif_port_get_pid(ofproto->backer->dpif,
+ ofp_port_to_odp_port(ofproto, flow->in_port));
+
+ return odp_put_userspace_action(pid, cookie, odp_actions);
+}
+
+static void
+compose_sflow_cookie(const struct ofproto_dpif *ofproto,
+ ovs_be16 vlan_tci, uint32_t odp_port,
+ unsigned int n_outputs, union user_action_cookie *cookie)
+{
+ int ifindex;
+
+ cookie->type = USER_ACTION_COOKIE_SFLOW;
+ cookie->sflow.vlan_tci = vlan_tci;
+
+ /* See http://www.sflow.org/sflow_version_5.txt (search for "Input/output
+ * port information") for the interpretation of cookie->output. */
+ switch (n_outputs) {
+ case 0:
+ /* 0x40000000 | 256 means "packet dropped for unknown reason". */
+ cookie->sflow.output = 0x40000000 | 256;
+ break;
+
+ case 1:
+ ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port);
+ if (ifindex) {
+ cookie->sflow.output = ifindex;
+ break;
+ }
+ /* Fall through. */
+ default:
+ /* 0x80000000 means "multiple output ports. */
+ cookie->sflow.output = 0x80000000 | n_outputs;
+ break;
+ }
+}
+
+/* Compose SAMPLE action for sFlow. */
+static size_t
+compose_sflow_action(const struct ofproto_dpif *ofproto,
+ struct ofpbuf *odp_actions,
+ const struct flow *flow,
+ uint32_t odp_port)
+{
+ uint32_t probability;
+ union user_action_cookie cookie;
+ size_t sample_offset, actions_offset;
+ int cookie_offset;
+
+ if (!ofproto->sflow || flow->in_port == OFPP_NONE) {
+ return 0;
+ }
+
+ sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE);
+
+ /* Number of packets out of UINT_MAX to sample. */
+ probability = dpif_sflow_get_probability(ofproto->sflow);
+ nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability);
+
+ actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS);
+ compose_sflow_cookie(ofproto, htons(0), odp_port,
+ odp_port == OVSP_NONE ? 0 : 1, &cookie);
+ cookie_offset = put_userspace_action(ofproto, odp_actions, flow, &cookie);
+
+ nl_msg_end_nested(odp_actions, actions_offset);
+ nl_msg_end_nested(odp_actions, sample_offset);
+ return cookie_offset;
+}
+
+/* SAMPLE action must be first action in any given list of actions.
+ * At this point we do not have all information required to build it. So try to
+ * build sample action as complete as possible. */
+static void
+add_sflow_action(struct action_xlate_ctx *ctx)
+{
+ ctx->user_cookie_offset = compose_sflow_action(ctx->ofproto,
+ ctx->odp_actions,
+ &ctx->flow, OVSP_NONE);
+ ctx->sflow_odp_port = 0;
+ ctx->sflow_n_outputs = 0;
+}
+
+/* Fix SAMPLE action according to data collected while composing ODP actions.
+ * We need to fix SAMPLE actions OVS_SAMPLE_ATTR_ACTIONS attribute, i.e. nested
+ * USERSPACE action's user-cookie which is required for sflow. */
+static void
+fix_sflow_action(struct action_xlate_ctx *ctx)
+{
+ const struct flow *base = &ctx->base_flow;
+ union user_action_cookie *cookie;
+
+ if (!ctx->user_cookie_offset) {
+ return;
+ }
+
+ cookie = ofpbuf_at(ctx->odp_actions, ctx->user_cookie_offset,
+ sizeof(*cookie));
+ assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
+
+ compose_sflow_cookie(ctx->ofproto, base->vlan_tci,
+ ctx->sflow_odp_port, ctx->sflow_n_outputs, cookie);
+}
+
+static void
+compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
+ bool check_stp)
+{
+ const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
+ uint32_t odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
+ ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
+ uint8_t flow_nw_tos = ctx->flow.nw_tos;
+ uint16_t out_port;
+
+ if (ofport) {
+ struct priority_to_dscp *pdscp;
+
+ if (ofport->up.pp.config & OFPUTIL_PC_NO_FWD) {
+ xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
+ return;
+ } else if (check_stp && !stp_forward_in_state(ofport->stp_state)) {
+ xlate_report(ctx, "STP not in forwarding state, skipping output");
+ return;
+ }
+
+ pdscp = get_priority(ofport, ctx->flow.skb_priority);
+ if (pdscp) {
+ ctx->flow.nw_tos &= ~IP_DSCP_MASK;
+ ctx->flow.nw_tos |= pdscp->dscp;
+ }
+ } else {
+ /* We may not 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. */
+ }
+
+ out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
+ ctx->flow.vlan_tci);
+ if (out_port != odp_port) {
+ ctx->flow.vlan_tci = htons(0);
+ }
+ 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;
+ ctx->sflow_n_outputs++;
+ ctx->nf_output_iface = ofp_port;
+ ctx->flow.vlan_tci = flow_vlan_tci;
+ ctx->flow.nw_tos = flow_nw_tos;
+}
+
+static void
+compose_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port)
+{
+ compose_output_action__(ctx, ofp_port, true);
+}
+
+static void
+xlate_table_action(struct action_xlate_ctx *ctx,
+ uint16_t in_port, uint8_t table_id, bool may_packet_in)
+{
+ if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
+ struct ofproto_dpif *ofproto = ctx->ofproto;
+ struct rule_dpif *rule;
+ uint16_t old_in_port;
+ uint8_t old_table_id;
+
+ old_table_id = ctx->table_id;
+ ctx->table_id = table_id;
+
+ /* Look up a flow with 'in_port' as the input port. */
+ old_in_port = ctx->flow.in_port;
+ ctx->flow.in_port = in_port;
+ rule = rule_dpif_lookup__(ofproto, &ctx->flow, table_id);
+
+ /* Tag the flow. */
+ if (table_id > 0 && table_id < N_TABLES) {
+ struct table_dpif *table = &ofproto->tables[table_id];
+ if (table->other_table) {
+ ctx->tags |= (rule && rule->tag
+ ? rule->tag
+ : rule_calculate_tag(&ctx->flow,
+ &table->other_table->mask,
+ table->basis));
+ }
+ }
+
+ /* Restore the original input port. Otherwise OFPP_NORMAL and
+ * OFPP_IN_PORT will have surprising behavior. */
+ ctx->flow.in_port = old_in_port;
+
+ if (ctx->resubmit_hook) {
+ ctx->resubmit_hook(ctx, rule);
+ }
+
+ if (rule == NULL && may_packet_in) {
+ /* TODO:XXX
+ * check if table configuration flags
+ * OFPTC_TABLE_MISS_CONTROLLER, default.
+ * OFPTC_TABLE_MISS_CONTINUE,
+ * OFPTC_TABLE_MISS_DROP
+ * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do?
+ */
+ rule = rule_dpif_miss_rule(ofproto, &ctx->flow);
+ }
+
+ if (rule) {
+ struct rule_dpif *old_rule = ctx->rule;
+
+ if (ctx->resubmit_stats) {
+ rule_credit_stats(rule, ctx->resubmit_stats);
+ }
+
+ ctx->recurse++;
+ ctx->rule = rule;
+ do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
+ ctx->rule = old_rule;
+ ctx->recurse--;
+ }
+
+ ctx->table_id = old_table_id;
+ } else {
+ static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+ VLOG_ERR_RL(&recurse_rl, "resubmit actions recursed over %d times",
+ MAX_RESUBMIT_RECURSION);
+ ctx->max_resubmit_trigger = true;
+ }
+}
+
+static void
+xlate_ofpact_resubmit(struct action_xlate_ctx *ctx,
+ const struct ofpact_resubmit *resubmit)
+{
+ uint16_t in_port;
+ uint8_t table_id;
+
+ in_port = resubmit->in_port;
+ if (in_port == OFPP_IN_PORT) {
+ in_port = ctx->flow.in_port;
+ }
+
+ table_id = resubmit->table_id;
+ if (table_id == 255) {
+ table_id = ctx->table_id;
+ }
+
+ xlate_table_action(ctx, in_port, table_id, false);
+}
+
+static void
+flood_packets(struct action_xlate_ctx *ctx, bool all)
+{
+ struct ofport_dpif *ofport;
+
+ HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) {
+ uint16_t ofp_port = ofport->up.ofp_port;
+
+ if (ofp_port == ctx->flow.in_port) {
+ continue;
+ }
+
+ if (all) {
+ compose_output_action__(ctx, ofp_port, false);
+ } else if (!(ofport->up.pp.config & OFPUTIL_PC_NO_FLOOD)) {
+ compose_output_action(ctx, ofp_port);
+ }
+ }
+
+ ctx->nf_output_iface = NF_OUT_FLOOD;
+}
+
+static void
+execute_controller_action(struct action_xlate_ctx *ctx, int len,
+ enum ofp_packet_in_reason reason,
+ uint16_t controller_id)
+{
+ struct ofputil_packet_in pin;
+ struct ofpbuf *packet;
+
+ ctx->slow |= SLOW_CONTROLLER;
+ if (!ctx->packet) {
+ return;
+ }
+
+ packet = ofpbuf_clone(ctx->packet);
+
+ if (packet->l2 && packet->l3) {
+ struct eth_header *eh;
+
+ eth_pop_vlan(packet);
+ eh = packet->l2;
+
+ /* If the Ethernet type is less than ETH_TYPE_MIN, it's likely an 802.2
+ * LLC frame. Calculating the Ethernet type of these frames is more
+ * trouble than seems appropriate for a simple assertion. */
+ assert(ntohs(eh->eth_type) < ETH_TYPE_MIN
+ || eh->eth_type == ctx->flow.dl_type);
+
+ memcpy(eh->eth_src, ctx->flow.dl_src, sizeof eh->eth_src);
+ memcpy(eh->eth_dst, ctx->flow.dl_dst, sizeof eh->eth_dst);
+
+ if (ctx->flow.vlan_tci & htons(VLAN_CFI)) {
+ eth_push_vlan(packet, ctx->flow.vlan_tci);
+ }
+
+ if (packet->l4) {
+ if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
+ packet_set_ipv4(packet, ctx->flow.nw_src, ctx->flow.nw_dst,
+ ctx->flow.nw_tos, ctx->flow.nw_ttl);
+ }
+
+ if (packet->l7) {
+ if (ctx->flow.nw_proto == IPPROTO_TCP) {
+ packet_set_tcp_port(packet, ctx->flow.tp_src,
+ ctx->flow.tp_dst);
+ } else if (ctx->flow.nw_proto == IPPROTO_UDP) {
+ packet_set_udp_port(packet, ctx->flow.tp_src,
+ ctx->flow.tp_dst);
+ }
+ }
+ }
+ }
+
+ pin.packet = packet->data;
+ pin.packet_len = packet->size;
+ pin.reason = reason;
+ pin.controller_id = controller_id;
+ pin.table_id = ctx->table_id;
+ pin.cookie = ctx->rule ? ctx->rule->up.flow_cookie : 0;
+
+ pin.send_len = len;
+ flow_get_metadata(&ctx->flow, &pin.fmd);
+
+ connmgr_send_packet_in(ctx->ofproto->up.connmgr, &pin);
+ ofpbuf_delete(packet);
+}
+
+static bool
+compose_dec_ttl(struct action_xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
+{
+ if (ctx->flow.dl_type != htons(ETH_TYPE_IP) &&
+ ctx->flow.dl_type != htons(ETH_TYPE_IPV6)) {
+ return false;
+ }
+
+ if (ctx->flow.nw_ttl > 1) {
+ ctx->flow.nw_ttl--;
+ return false;
+ } else {
+ size_t i;
+
+ for (i = 0; i < ids->n_controllers; i++) {
+ execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
+ ids->cnt_ids[i]);
+ }
+
+ /* Stop processing for current table. */
+ return true;
+ }
+}
+
+static void
+xlate_output_action(struct action_xlate_ctx *ctx,
+ uint16_t port, uint16_t max_len, bool may_packet_in)
+{
+ uint16_t prev_nf_output_iface = ctx->nf_output_iface;
+
+ ctx->nf_output_iface = NF_OUT_DROP;
+
+ switch (port) {
+ case OFPP_IN_PORT:
+ compose_output_action(ctx, ctx->flow.in_port);
+ break;
+ case OFPP_TABLE:
+ xlate_table_action(ctx, ctx->flow.in_port, 0, may_packet_in);
+ break;
+ case OFPP_NORMAL:
+ xlate_normal(ctx);
+ break;
+ case OFPP_FLOOD:
+ flood_packets(ctx, false);
+ break;
+ case OFPP_ALL:
+ flood_packets(ctx, true);
+ break;
+ case OFPP_CONTROLLER:
+ execute_controller_action(ctx, max_len, OFPR_ACTION, 0);
+ break;
+ case OFPP_NONE:
+ break;
+ case OFPP_LOCAL:
+ default:
+ if (port != ctx->flow.in_port) {
+ compose_output_action(ctx, port);
+ } else {
+ xlate_report(ctx, "skipping output to input port");
+ }
+ 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_output_reg_action(struct action_xlate_ctx *ctx,
+ const struct ofpact_output_reg *or)
+{
+ uint64_t port = mf_get_subfield(&or->src, &ctx->flow);
+ if (port <= UINT16_MAX) {
+ xlate_output_action(ctx, port, or->max_len, false);
+ }
+}
+
+static void
+xlate_enqueue_action(struct action_xlate_ctx *ctx,
+ const struct ofpact_enqueue *enqueue)
+{
+ uint16_t ofp_port = enqueue->port;
+ uint32_t queue_id = enqueue->queue;
+ uint32_t flow_priority, priority;
+ int error;
+
+ /* Translate queue to priority. */
+ error = dpif_queue_to_priority(ctx->ofproto->backer->dpif,
+ queue_id, &priority);
+ if (error) {
+ /* Fall back to ordinary output action. */
+ xlate_output_action(ctx, enqueue->port, 0, false);
+ return;
+ }
+
+ /* Check output port. */
+ if (ofp_port == OFPP_IN_PORT) {
+ ofp_port = ctx->flow.in_port;
+ } else if (ofp_port == ctx->flow.in_port) {
+ return;
+ }
+
+ /* Add datapath actions. */
+ flow_priority = ctx->flow.skb_priority;
+ ctx->flow.skb_priority = priority;
+ compose_output_action(ctx, ofp_port);
+ ctx->flow.skb_priority = flow_priority;
+
+ /* Update NetFlow output port. */
+ if (ctx->nf_output_iface == NF_OUT_DROP) {
+ ctx->nf_output_iface = ofp_port;
+ } else if (ctx->nf_output_iface != NF_OUT_FLOOD) {
+ ctx->nf_output_iface = NF_OUT_MULTI;
+ }
+}
+
+static void
+xlate_set_queue_action(struct action_xlate_ctx *ctx, uint32_t queue_id)
+{
+ uint32_t skb_priority;
+
+ if (!dpif_queue_to_priority(ctx->ofproto->backer->dpif,
+ queue_id, &skb_priority)) {
+ ctx->flow.skb_priority = skb_priority;
+ } else {
+ /* Couldn't translate queue to a priority. Nothing to do. A warning
+ * has already been logged. */
+ }
+}
+
+struct xlate_reg_state {
+ ovs_be16 vlan_tci;
+ ovs_be64 tun_id;
+};
+
+static void
+xlate_autopath(struct action_xlate_ctx *ctx,
+ const struct ofpact_autopath *ap)
+{
+ uint16_t ofp_port = ap->port;
+ struct ofport_dpif *port = get_ofp_port(ctx->ofproto, ofp_port);
+
+ if (!port || !port->bundle) {
+ ofp_port = OFPP_NONE;
+ } else if (port->bundle->bond) {
+ /* Autopath does not support VLAN hashing. */
+ struct ofport_dpif *slave = bond_choose_output_slave(
+ port->bundle->bond, &ctx->flow, 0, &ctx->tags);
+ if (slave) {
+ ofp_port = slave->up.ofp_port;
+ }
+ }
+ nxm_reg_load(&ap->dst, ofp_port, &ctx->flow);
+}
+
+static bool
+slave_enabled_cb(uint16_t ofp_port, void *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_;
+ struct ofport_dpif *port;
+
+ switch (ofp_port) {
+ case OFPP_IN_PORT:
+ case OFPP_TABLE:
+ case OFPP_NORMAL:
+ case OFPP_FLOOD:
+ case OFPP_ALL:
+ case OFPP_NONE:
+ return true;
+ case OFPP_CONTROLLER: /* Not supported by the bundle action. */
+ return false;
+ default:
+ port = get_ofp_port(ofproto, ofp_port);
+ return port ? port->may_enable : false;
+ }
+}
+
+static void
+xlate_bundle_action(struct action_xlate_ctx *ctx,
+ const struct ofpact_bundle *bundle)
+{
+ uint16_t port;
+
+ port = bundle_execute(bundle, &ctx->flow, slave_enabled_cb, ctx->ofproto);
+ if (bundle->dst.field) {
+ nxm_reg_load(&bundle->dst, port, &ctx->flow);
+ } else {
+ xlate_output_action(ctx, port, 0, false);
+ }
+}
+
+static void
+xlate_learn_action(struct action_xlate_ctx *ctx,
+ const struct ofpact_learn *learn)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ struct ofputil_flow_mod fm;
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
+ int error;
+
+ ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ learn_execute(learn, &ctx->flow, &fm, &ofpacts);
+
+ error = ofproto_flow_mod(&ctx->ofproto->up, &fm);
+ if (error && !VLOG_DROP_WARN(&rl)) {
+ VLOG_WARN("learning action failed to modify flow table (%s)",
+ ofperr_get_name(error));
+ }
+
+ ofpbuf_uninit(&ofpacts);
+}
+
+/* Reduces '*timeout' to no more than 'max'. A value of zero in either case
+ * means "infinite". */
+static void
+reduce_timeout(uint16_t max, uint16_t *timeout)
+{
+ if (max && (!*timeout || *timeout > max)) {
+ *timeout = max;
+ }
+}
+
+static void
+xlate_fin_timeout(struct action_xlate_ctx *ctx,
+ const struct ofpact_fin_timeout *oft)
+{
+ if (ctx->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
+ struct rule_dpif *rule = ctx->rule;
+
+ reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
+ reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
+ }
+}
+
+static bool
+may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
+{
+ if (port->up.pp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp)
+ ? OFPUTIL_PC_NO_RECV_STP
+ : OFPUTIL_PC_NO_RECV)) {
+ return false;
+ }
+
+ /* Only drop packets here if both forwarding and learning are
+ * disabled. If just learning is enabled, we need to have
+ * OFPP_NORMAL and the learning action have a look at the packet
+ * before we can drop it. */
+ if (!stp_forward_in_state(port->stp_state)
+ && !stp_learn_in_state(port->stp_state)) {
+ return false;
+ }
+
+ return true;
+}
+
+static void
+do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+ struct action_xlate_ctx *ctx)
+{
+ const struct ofport_dpif *port;
+ bool was_evictable = true;
+ const struct ofpact *a;
+
+ port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
+ if (port && !may_receive(port, ctx)) {
+ /* Drop this flow. */
+ return;
+ }
+
+ if (ctx->rule) {
+ /* Don't let the rule we're working on get evicted underneath us. */
+ was_evictable = ctx->rule->up.evictable;
+ ctx->rule->up.evictable = false;
+ }
+ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+ struct ofpact_controller *controller;
+ const struct ofpact_metadata *metadata;
+
+ if (ctx->exit) {
+ break;
+ }
+
+ switch (a->type) {
+ case OFPACT_OUTPUT:
+ xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
+ ofpact_get_OUTPUT(a)->max_len, true);
+ break;
+
+ case OFPACT_CONTROLLER:
+ controller = ofpact_get_CONTROLLER(a);
+ execute_controller_action(ctx, controller->max_len,
+ controller->reason,
+ controller->controller_id);
+ break;
+
+ case OFPACT_ENQUEUE:
+ xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a));
+ break;
+
+ case OFPACT_SET_VLAN_VID:
+ ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+ ctx->flow.vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
+ | htons(VLAN_CFI));
+ break;
+
+ case OFPACT_SET_VLAN_PCP:
+ ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+ ctx->flow.vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
+ << VLAN_PCP_SHIFT)
+ | VLAN_CFI);
+ break;
+
+ case OFPACT_STRIP_VLAN:
+ ctx->flow.vlan_tci = htons(0);
+ break;
+
+ case OFPACT_PUSH_VLAN:
+ /* TODO:XXX 802.1AD(QinQ) */
+ ctx->flow.vlan_tci = htons(VLAN_CFI);
+ break;
+
+ case OFPACT_SET_ETH_SRC:
+ memcpy(ctx->flow.dl_src, ofpact_get_SET_ETH_SRC(a)->mac,
+ ETH_ADDR_LEN);
+ break;
+
+ case OFPACT_SET_ETH_DST:
+ memcpy(ctx->flow.dl_dst, ofpact_get_SET_ETH_DST(a)->mac,
+ ETH_ADDR_LEN);
+ break;
+
+ case OFPACT_SET_IPV4_SRC:
+ ctx->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+ break;
+
+ case OFPACT_SET_IPV4_DST:
+ ctx->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
+ break;
+
+ case OFPACT_SET_IPV4_DSCP:
+ /* OpenFlow 1.0 only supports IPv4. */
+ if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
+ ctx->flow.nw_tos &= ~IP_DSCP_MASK;
+ ctx->flow.nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp;
+ }
+ break;
+
+ case OFPACT_SET_L4_SRC_PORT:
+ ctx->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
+ break;
+
+ case OFPACT_SET_L4_DST_PORT:
+ ctx->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
+ break;
+
+ case OFPACT_RESUBMIT:
+ xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
+ break;
+
+ case OFPACT_SET_TUNNEL:
+ ctx->flow.tunnel.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
+ break;
+
+ case OFPACT_SET_QUEUE:
+ xlate_set_queue_action(ctx, ofpact_get_SET_QUEUE(a)->queue_id);
+ break;
+
+ case OFPACT_POP_QUEUE:
+ ctx->flow.skb_priority = ctx->orig_skb_priority;
+ break;
+
+ case OFPACT_REG_MOVE:
+ nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->flow);
+ break;
+
+ case OFPACT_REG_LOAD:
+ nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
+ break;