+/* 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;
+ struct ofpbuf *reply;
+
+ run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
+ recv_xid = ((struct ofp_header *) reply->data)->xid;
+ if (send_xid == recv_xid) {
+ const struct ofputil_msg_type *type;
+ const struct ofp_stats_msg *osm;
+ enum ofputil_msg_code code;
+
+ ofputil_decode_msg_type(reply->data, &type);
+ code = ofputil_msg_type_code(type);
+ if (code != OFPUTIL_OFPST_FLOW_REPLY &&
+ code != OFPUTIL_NXST_FLOW_REPLY) {
+ ovs_fatal(0, "received bad reply: %s",
+ ofp_to_string(reply->data, reply->size,
+ verbosity + 1));
+ }
+
+ osm = reply->data;
+ if (!(osm->flags & htons(OFPSF_REPLY_MORE))) {
+ done = true;
+ }
+
+ for (;;) {
+ struct fte_version *version;
+ struct ofputil_flow_stats fs;
+ int retval;
+
+ retval = ofputil_decode_flow_stats_reply(&fs, reply, false);
+ if (retval) {
+ if (retval != EOF) {
+ ovs_fatal(0, "parse error in reply");
+ }
+ break;
+ }
+
+ version = xmalloc(sizeof *version);
+ version->cookie = fs.cookie;
+ version->idle_timeout = fs.idle_timeout;
+ version->hard_timeout = fs.hard_timeout;
+ version->flags = 0;
+ version->n_actions = fs.n_actions;
+ version->actions = xmemdup(fs.actions,
+ fs.n_actions * sizeof *fs.actions);
+
+ fte_insert(cls, &fs.rule, version, index);
+ }
+ } else {
+ VLOG_DBG("received reply with xid %08"PRIx32" "
+ "!= expected %08"PRIx32, recv_xid, send_xid);
+ }
+ ofpbuf_delete(reply);
+ }
+}
+
+static void
+fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
+ enum ofputil_protocol protocol, struct list *packets)
+{
+ const struct fte_version *version = fte->versions[index];
+ struct ofputil_flow_mod fm;
+ struct ofpbuf *ofm;
+
+ fm.cr = fte->rule;
+ fm.cookie = version->cookie;
+ fm.table_id = 0xff;
+ fm.command = command;
+ fm.idle_timeout = version->idle_timeout;
+ fm.hard_timeout = version->hard_timeout;
+ fm.buffer_id = UINT32_MAX;
+ fm.out_port = OFPP_NONE;
+ fm.flags = version->flags;
+ if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
+ command == OFPFC_MODIFY_STRICT) {
+ fm.actions = version->actions;
+ fm.n_actions = version->n_actions;
+ } else {
+ fm.actions = NULL;
+ fm.n_actions = 0;
+ }
+
+ ofm = ofputil_encode_flow_mod(&fm, protocol);
+ list_push_back(packets, &ofm->list_node);
+}
+
+static void
+do_replace_flows(int argc OVS_UNUSED, char *argv[])
+{
+ enum { FILE_IDX = 0, SWITCH_IDX = 1 };
+ enum ofputil_protocol usable_protocols, protocol;
+ struct cls_cursor cursor;
+ struct classifier cls;
+ struct list requests;
+ struct vconn *vconn;
+ struct fte *fte;
+
+ classifier_init(&cls);
+ usable_protocols = read_flows_from_file(argv[2], &cls, FILE_IDX);
+
+ protocol = open_vconn(argv[1], &vconn);
+ protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
+
+ read_flows_from_switch(vconn, protocol, &cls, SWITCH_IDX);
+
+ list_init(&requests);
+
+ /* Delete flows that exist on the switch but not in the file. */
+ cls_cursor_init(&cursor, &cls, NULL);
+ CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
+ struct fte_version *file_ver = fte->versions[FILE_IDX];
+ struct fte_version *sw_ver = fte->versions[SWITCH_IDX];
+
+ if (sw_ver && !file_ver) {
+ fte_make_flow_mod(fte, SWITCH_IDX, OFPFC_DELETE_STRICT,
+ protocol, &requests);
+ }
+ }
+
+ /* Add flows that exist in the file but not on the switch.
+ * Update flows that exist in both places but differ. */
+ cls_cursor_init(&cursor, &cls, NULL);
+ CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
+ struct fte_version *file_ver = fte->versions[FILE_IDX];
+ struct fte_version *sw_ver = fte->versions[SWITCH_IDX];
+
+ if (file_ver
+ && (readd || !sw_ver || !fte_version_equals(sw_ver, file_ver))) {
+ fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests);
+ }
+ }
+ transact_multiple_noreply(vconn, &requests);
+ vconn_close(vconn);
+
+ fte_free_all(&cls);
+}
+
+static void
+read_flows_from_source(const char *source, struct classifier *cls, int index)
+{
+ struct stat s;
+
+ if (source[0] == '/' || source[0] == '.'
+ || (!strchr(source, ':') && !stat(source, &s))) {
+ read_flows_from_file(source, cls, index);
+ } else {
+ enum ofputil_protocol protocol;
+ struct vconn *vconn;
+
+ protocol = open_vconn(source, &vconn);
+ protocol = set_protocol_for_flow_dump(vconn, protocol, OFPUTIL_P_ANY);
+ read_flows_from_switch(vconn, protocol, cls, index);
+ vconn_close(vconn);
+ }
+}
+
+static void
+do_diff_flows(int argc OVS_UNUSED, char *argv[])
+{
+ bool differences = false;
+ struct cls_cursor cursor;
+ struct classifier cls;
+ struct fte *fte;
+
+ classifier_init(&cls);
+ read_flows_from_source(argv[1], &cls, 0);
+ read_flows_from_source(argv[2], &cls, 1);
+
+ cls_cursor_init(&cursor, &cls, NULL);
+ CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
+ struct fte_version *a = fte->versions[0];
+ struct fte_version *b = fte->versions[1];
+
+ if (!a || !b || !fte_version_equals(a, b)) {
+ char *rule_s = cls_rule_to_string(&fte->rule);
+ if (a) {
+ printf("-%s", rule_s);
+ fte_version_print(a);
+ }
+ if (b) {
+ printf("+%s", rule_s);
+ fte_version_print(b);
+ }
+ free(rule_s);
+
+ differences = true;
+ }
+ }
+
+ fte_free_all(&cls);
+
+ if (differences) {
+ exit(2);
+ }
+}
+\f
+/* Undocumented commands for unit testing. */
+
+static void
+do_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
+{
+ enum ofputil_protocol usable_protocols;
+ enum ofputil_protocol protocol = 0;
+ char *usable_s;
+ size_t i;
+
+ usable_protocols = ofputil_flow_mod_usable_protocols(fms, n_fms);
+ usable_s = ofputil_protocols_to_string(usable_protocols);
+ printf("usable protocols: %s\n", usable_s);
+ free(usable_s);
+
+ if (!(usable_protocols & allowed_protocols)) {
+ ovs_fatal(0, "no usable protocol");
+ }
+ for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+ protocol = 1 << i;
+ if (protocol & usable_protocols & allowed_protocols) {
+ break;
+ }
+ }
+ assert(IS_POW2(protocol));
+
+ printf("chosen protocol: %s\n", ofputil_protocol_to_string(protocol));
+
+ for (i = 0; i < n_fms; i++) {
+ struct ofputil_flow_mod *fm = &fms[i];
+ struct ofpbuf *msg;
+
+ msg = ofputil_encode_flow_mod(fm, protocol);
+ ofp_print(stdout, msg->data, msg->size, verbosity);
+ ofpbuf_delete(msg);
+
+ free(fm->actions);
+ }
+}
+
+/* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints
+ * it back to stdout. */
+static void
+do_parse_flow(int argc OVS_UNUSED, char *argv[])
+{
+ struct ofputil_flow_mod fm;
+
+ parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
+ do_parse_flows__(&fm, 1);
+}
+
+/* "parse-flows FILENAME": reads the named file as a sequence of flows (like
+ * add-flows) and prints each of the flows back to stdout. */
+static void
+do_parse_flows(int argc OVS_UNUSED, char *argv[])
+{
+ struct ofputil_flow_mod *fms = NULL;
+ size_t n_fms = 0;
+
+ parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
+ do_parse_flows__(fms, n_fms);
+ free(fms);
+}
+
+/* "parse-nx-match": reads a series of nx_match specifications as strings from
+ * stdin, does some internal fussing with them, and then prints them back as
+ * strings on stdout. */