+subfacet_destroy__(struct ofproto_dpif *ofproto, struct subfacet *subfacet)
+{
+ subfacet_uninstall(ofproto, subfacet);
+ hmap_remove(&ofproto->subfacets, &subfacet->hmap_node);
+ list_remove(&subfacet->list_node);
+ free(subfacet->key);
+ free(subfacet->actions);
+ 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 ofproto_dpif *ofproto, 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(ofproto, facet);
+ } else {
+ subfacet_destroy__(ofproto, subfacet);
+ }
+}
+
+/* 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) {
+ ofpbuf_use_stack(key, keybuf, sizeof *keybuf);
+ odp_flow_key_from_flow(key, &subfacet->facet->flow);
+ } else {
+ ofpbuf_use_const(key, subfacet->key, subfacet->key_len);
+ }
+}
+
+/* Composes the datapath actions for 'subfacet' based on its rule's actions. */
+static void
+subfacet_make_actions(struct ofproto_dpif *p, struct subfacet *subfacet,
+ const struct ofpbuf *packet)
+{
+ struct facet *facet = subfacet->facet;
+ const struct rule_dpif *rule = facet->rule;
+ struct ofpbuf *odp_actions;
+ struct action_xlate_ctx ctx;
+
+ action_xlate_ctx_init(&ctx, p, &facet->flow, subfacet->initial_tci,
+ rule->up.flow_cookie, packet);
+ odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
+ facet->tags = ctx.tags;
+ facet->may_install = ctx.may_set_up_flow;
+ 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)) {
+ free(subfacet->actions);
+ subfacet->actions_len = odp_actions->size;
+ subfacet->actions = xmemdup(odp_actions->data, odp_actions->size);
+ }
+
+ ofpbuf_delete(odp_actions);
+}
+
+/* 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 ofproto_dpif *ofproto, struct subfacet *subfacet,
+ const struct nlattr *actions, size_t actions_len,
+ struct dpif_flow_stats *stats)
+{
+ 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;
+ }
+
+ subfacet_get_key(subfacet, &keybuf, &key);
+ ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
+ actions, actions_len, stats);
+
+ if (stats) {
+ subfacet_reset_dp_stats(subfacet, stats);
+ }
+
+ return ret;
+}
+
+/* If 'subfacet' is installed in the datapath, uninstalls it. */
+static void
+subfacet_uninstall(struct ofproto_dpif *p, struct subfacet *subfacet)
+{
+ if (subfacet->installed) {
+ struct odputil_keybuf keybuf;
+ struct dpif_flow_stats stats;
+ struct ofpbuf key;
+ int error;
+
+ subfacet_get_key(subfacet, &keybuf, &key);
+ error = dpif_flow_del(p->dpif, key.data, key.size, &stats);
+ subfacet_reset_dp_stats(subfacet, &stats);
+ if (!error) {
+ subfacet_update_stats(p, subfacet, &stats);
+ }
+ subfacet->installed = false;
+ } 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 ofproto_dpif *ofproto, struct subfacet *subfacet,
+ long long int used)
+{
+ if (used > subfacet->used) {
+ subfacet->used = used;
+ facet_update_time(ofproto, 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 ofproto_dpif *ofproto, 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(ofproto, subfacet, stats->used);
+ facet->packet_count += stats->n_packets;
+ facet->byte_count += stats->n_bytes;
+ 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,
+ 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];
+ 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 void
+complete_operation(struct rule_dpif *rule)