+/* Prints 'version' on stdout. Expects the caller to have printed the rule
+ * associated with the version. */
+static void
+fte_version_print(const struct fte_version *version)
+{
+ struct ds s;
+
+ if (version->cookie != htonll(0)) {
+ printf(" cookie=0x%"PRIx64, ntohll(version->cookie));
+ }
+ if (version->idle_timeout != OFP_FLOW_PERMANENT) {
+ printf(" idle_timeout=%"PRIu16, version->idle_timeout);
+ }
+ if (version->hard_timeout != OFP_FLOW_PERMANENT) {
+ printf(" hard_timeout=%"PRIu16, version->hard_timeout);
+ }
+
+ ds_init(&s);
+ ofp_print_actions(&s, version->actions, version->n_actions);
+ printf(" %s\n", ds_cstr(&s));
+ ds_destroy(&s);
+}
+
+static struct fte *
+fte_from_cls_rule(const struct cls_rule *cls_rule)
+{
+ return cls_rule ? CONTAINER_OF(cls_rule, struct fte, rule) : NULL;
+}
+
+/* Frees 'fte' and its versions. */
+static void
+fte_free(struct fte *fte)
+{
+ if (fte) {
+ fte_version_free(fte->versions[0]);
+ fte_version_free(fte->versions[1]);
+ free(fte);
+ }
+}
+
+/* Frees all of the FTEs within 'cls'. */
+static void
+fte_free_all(struct classifier *cls)
+{
+ struct cls_cursor cursor;
+ struct fte *fte, *next;
+
+ cls_cursor_init(&cursor, cls, NULL);
+ CLS_CURSOR_FOR_EACH_SAFE (fte, next, rule, &cursor) {
+ classifier_remove(cls, &fte->rule);
+ fte_free(fte);
+ }
+}
+
+/* Searches 'cls' for an FTE matching 'rule', inserting a new one if
+ * necessary. Sets 'version' as the version of that rule with the given
+ * 'index', replacing any existing version, if any.
+ *
+ * Takes ownership of 'version'. */
+static void
+fte_insert(struct classifier *cls, const struct cls_rule *rule,
+ struct fte_version *version, int index)
+{
+ struct fte *old, *fte;
+
+ fte = xzalloc(sizeof *fte);
+ fte->rule = *rule;
+ fte->versions[index] = version;
+
+ old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
+ if (old) {
+ fte_version_free(old->versions[index]);
+ fte->versions[!index] = old->versions[!index];
+ free(old);
+ }
+}
+
+/* Reads the flows in 'filename' as flow table entries in 'cls' for the version
+ * with the specified 'index'. Returns the flow formats able to represent the
+ * flows that were read. */
+static enum ofputil_protocol
+read_flows_from_file(const char *filename, struct classifier *cls, int index)
+{
+ enum ofputil_protocol usable_protocols;
+ struct ds s;
+ FILE *file;
+
+ file = !strcmp(filename, "-") ? stdin : fopen(filename, "r");
+ if (file == NULL) {
+ ovs_fatal(errno, "%s: open", filename);
+ }
+
+ ds_init(&s);
+ usable_protocols = OFPUTIL_P_ANY;
+ while (!ds_get_preprocessed_line(&s, file)) {
+ struct fte_version *version;
+ struct ofputil_flow_mod fm;
+
+ parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
+
+ version = xmalloc(sizeof *version);
+ version->cookie = fm.cookie;
+ version->idle_timeout = fm.idle_timeout;
+ version->hard_timeout = fm.hard_timeout;
+ version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF_EMERG);
+ version->actions = fm.actions;
+ version->n_actions = fm.n_actions;
+
+ usable_protocols &= ofputil_usable_protocols(&fm.cr);
+
+ fte_insert(cls, &fm.cr, version, index);
+ }
+ ds_destroy(&s);
+
+ if (file != stdin) {
+ fclose(file);
+ }
+
+ return usable_protocols;
+}
+
+/* Reads the OpenFlow flow table from 'vconn', which has currently active flow
+ * format 'protocol', and adds them as flow table entries in 'cls' for the
+ * version with the specified 'index'. */
+static void
+read_flows_from_switch(struct vconn *vconn,
+ enum ofputil_protocol protocol,
+ struct classifier *cls, int index)
+{
+ struct ofputil_flow_stats_request fsr;
+ struct ofpbuf *request;
+ ovs_be32 send_xid;
+ bool done;
+
+ fsr.aggregate = false;
+ cls_rule_init_catchall(&fsr.match, 0);
+ fsr.out_port = OFPP_NONE;
+ fsr.table_id = 0xff;
+ fsr.cookie = fsr.cookie_mask = htonll(0);
+ request = ofputil_encode_flow_stats_request(&fsr, protocol);
+ send_xid = ((struct ofp_header *) request->data)->xid;
+ send_openflow_buffer(vconn, request);
+
+ done = false;
+ while (!done) {
+ ovs_be32 recv_xid;