From: Shashank Shanbhag <shashank.shanb...@gmail.com>

Fix replace-flows and diff-flows to modify/diff flows in multiple tables.
Add a --tables option that allows the user to specify a comma-separated
list of table indexes to replace/diff.

Signed-off-by: Shashank Shanbhag <shashank.shanb...@gmail.com>
Acked-by: Romain Lenglet <romain.leng...@oracle.com>
---
 AUTHORS                  |   1 +
 NEWS                     |   5 +-
 tests/ovs-ofctl.at       |  69 +++++++-
 utilities/ovs-ofctl.8.in |  20 ++-
 utilities/ovs-ofctl.c    | 414 ++++++++++++++++++++++++++++++++++++++++-------
 5 files changed, 437 insertions(+), 72 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 16e76b4..54705a1 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -153,6 +153,7 @@ Scott Lowe              scott.l...@scottlowe.org
 Scott Mann              sdm...@gmail.com
 Selvamuthukumar         smku...@merunetworks.com
 Shan Wei                davids...@tencent.com
+Shashank Shanbhag       shashank.shanb...@gmail.com
 Shih-Hao Li             s...@nicira.com
 Shu Shen                shu.s...@radisys.com
 Simon Horman            ho...@verge.net.au
diff --git a/NEWS b/NEWS
index 882a381..9d3c46b 100644
--- a/NEWS
+++ b/NEWS
@@ -81,7 +81,10 @@ Post-v2.3.0
      openvswitch.ko but built and loaded automatically as individual kernel
      modules (vport-*.ko).
    - Support for STT tunneling.
-
+   - ovs-ofctl: replace-flows and diff-flows now operate on multiple tables.
+   - ovs-ofctl: --tables option added to replace-flows and diff-flows to allow
+     specific tables to be replaced or diffed.
+   - ovs-ofctl works for OF version 1.3 and above.
 
 v2.3.0 - 14 Aug 2014
 ---------------------
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 42be8f0..ec680bb 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -2789,27 +2789,82 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sed 
'/ST_FLOW reply/d' | sort
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-
 dnl Importance parameter added in OF1.4.
 dnl This validates whether flows with importance
 dnl parameter are getting replaced with "replace-flows" or
 dnl not by validating dump-flows output after replace with the expected output.
+dnl MOD_FLOW does not modify importance field - ONF EXT-496.
 
 AT_SETUP([ovs-ofctl replace-flows with importance])
 OVS_VSWITCHD_START
 
 dnl Add flows to br0 with importance via OF1.4+. For more details refer 
"ovs-ofctl rule with importance" test case.
-for i in 1 2 3 4 5 6 7 8; do echo "dl_vlan=$i,importance=$i,actions=drop"; 
done > add-flows.txt
+for i in 1 2 3 4 5 6 7 8; do echo "dl_vlan=$i,importance=$i actions=drop"; 
done | sort > add-flows.txt
 AT_CHECK([ovs-ofctl -O OpenFlow14 add-flows br0 add-flows.txt])
 
-dnl Replace some flows in the bridge.
-for i in 1 3 5 7; do echo "dl_vlan=$i,importance=`expr $i + 10`,actions=drop"; 
done > replace-flows.txt
+dnl Modify odd numbered flows. Leave even numbered ones alone.
+for i in 1 2 3 4 5 6 7 8; do if [[ `expr $i % 2` -eq 1 ]]; then 
importance=`expr $i + 10`; else importance=$i; fi; echo " 
importance=$importance, dl_vlan=$i actions=drop"; done | sort > 
replace-flows.txt
+AT_CHECK([ovs-ofctl -O OpenFlow14 replace-flows br0 replace-flows.txt])
+
+dnl Dump the flows and compare them against expected output.
+
+for i in 1 2 3 4 5 6 7 8; do echo " importance=$i, dl_vlan=$i actions=drop"; 
done | sort > expout
+AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed 
'/OFPST_FLOW/d' | sort], [0], [expout])
+
+dnl Replace some flows in the bridge. Delete flows that are not present in 
replace-flows.txt
+dnl for i in 1 3 5 7; do echo "dl_vlan=$i,importance=`expr $i + 10` 
actions=drop"; done | sort > replace-flows.txt
+
+for i in 1 3 5 7; do echo "dl_vlan=$i,importance=$i actions=drop"; done | sort 
> replace-flows.txt
 AT_CHECK([ovs-ofctl -O OpenFlow14 replace-flows br0 replace-flows.txt])
 
 dnl Dump them and compare the dump flows output against the expected output.
-for i in 1 2 3 4 5 6 7 8; do if [[ `expr $i % 2` -eq 1 ]]; then 
importance=`expr $i + 10`; else importance=$i; fi; echo " 
importance=$importance, dl_vlan=$i actions=drop"; done | sort > expout
-AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed 
'/OFPST_FLOW/d' | sort],
-  [0], [expout])
+for i in 1 3 5 7; do echo " importance=$i, dl_vlan=$i actions=drop"; done | 
sort > expout
+AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed 
'/OFPST_FLOW/d' | sort], [0], [expout])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl --tables option added to ovs-ofctl "replace-flows" and "diff-flows".
+dnl This validates whether flows in table IDs specified with --tables
+dnl are getting replaced with "replace-flows" or not by validating
+dnl dump-flows output after replace with the expected output.
+
+AT_SETUP([ovs-ofctl replace-flows with --tables])
+OVS_VSWITCHD_START
+
+dnl Add flows to br0 for tables 1 to 8.
+for i in 1 2 3 4 5 6 7 8; do echo "table=$i,dl_vlan=$i,actions=drop"; done > 
add-flows.txt
+AT_CHECK([ovs-ofctl -O OpenFlow14 add-flows br0 add-flows.txt])
+
+dnl Replace flows from tables 1,3,5,7 in the bridge.
+for i in 1 3 5 7; do echo 
"table=$i,ip,nw_dst=192.168.1.$i,dl_vlan=$i,actions=drop"; done > 
replace-flows.txt
+AT_CHECK([ovs-ofctl -O OpenFlow14 replace-flows br0 replace-flows.txt --tables 
1,5,7,3])
+
+dnl Generate the expout.
+for i in 1 2 3 4 5 6 7 8; do if [[ `expr $i % 2` -eq 1 ]]; then echo " 
table=$i, ip,dl_vlan=$i,nw_dst=192.168.1.$i actions=drop"; else echo " 
table=$i, dl_vlan=$i actions=drop"; fi; done | sort > expout
+dnl Dump the flows and check.
+AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed 
'/OFPST_FLOW/d' | sort], [0], [expout])
+
+AT_DATA([oneflow.txt], [[
+table=4,ip,nw_dst=10.0.0.4,actions=output:100
+]])
+
+dnl Check that all flows except the ones in the replacement file are present 
after
+dnl replace-flows is executed.
+AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br0])
+AT_CHECK([ovs-ofctl -O OpenFlow14 add-flows br0 add-flows.txt])
+AT_CHECK([ovs-ofctl -O OpenFlow14 replace-flows br0 oneflow.txt])
+AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed 
'/OFPST_FLOW/d' | sort], [0], [dnl
+ table=4, ip,nw_dst=10.0.0.4 actions=output:100
+])
+
+dnl Check that tables that do not have flows are not replaced.
+dnl Check that existing flows in other tables are not replaced as well.
+AT_CHECK([ovs-ofctl -O OpenFlow14 del-flows br0])
+AT_CHECK([ovs-ofctl -O OpenFlow14 add-flows br0 add-flows.txt])
+AT_CHECK([ovs-ofctl -O OpenFlow14 replace-flows br0 oneflow.txt --tables 100])
+for i in 1 2 3 4 5 6 7 8; do echo " table=$i, dl_vlan=$i actions=drop"; done | 
sort > expout
+AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed 
'/OFPST_FLOW/d' | sort], [0], [expout])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index c667aa4..ce606eb 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -318,7 +318,7 @@ Deletes entries from \fIswitch\fR's flow table.  With only a
 entries that match the specified flows.  With \fB\-\-strict\fR,
 wildcards are not treated as active for matching purposes.
 .
-.IP "[\fB\-\-readd\fR] \fBreplace\-flows \fIswitch file\fR"
+.IP "[\fB\-\-readd\fR] \fBreplace\-flows \fIswitch file\fR [\fB\-\-tables 
\fltable-ids\fR]"
 Reads flow entries from \fIfile\fR (or \fBstdin\fR if \fIfile\fR is
 \fB\-\fR) and queries the flow table from \fIswitch\fR.  Then it fixes
 up any differences, adding flows from \fIflow\fR that are missing on
@@ -327,12 +327,21 @@ up any differences, adding flows from \fIflow\fR that are 
missing on
 or timeouts differ in \fIfile\fR.
 .
 .IP
+With \fB\-\-tables\fR, \fBovs\-ofctl\fR only considers flows from the
+comma-separated table IDs specified. The tables are replaced
+in the specified order, e.g. (\fB\-\-tables 1,3,4,2\fR). The valid
+range of table IDs is 0 to 254. If a table ID is not specified,
+the table with that ID is ignored and no flows are replaced in that
+table in the switch. If \fB\-\-tables\fR is not specified, all tables,
+0 - 254, will be replaced.
+.
+.IP
 With \fB\-\-readd\fR, \fBovs\-ofctl\fR adds all the flows from
 \fIfile\fR, even those that exist with the same actions, cookie, and
 timeout in \fIswitch\fR.  This resets all the flow packet and byte
 counters to 0, which can be useful for debugging.
 .
-.IP "\fBdiff\-flows \fIsource1 source2\fR"
+.IP "\fBdiff\-flows \fIsource1 source2\fR [\fB\-\-tables \fltable-ids\fR]"
 Reads flow entries from \fIsource1\fR and \fIsource2\fR and prints the
 differences.  A flow that is in \fIsource1\fR but not in \fIsource2\fR
 is printed preceded by a \fB\-\fR, and a flow that is in \fIsource2\fR
@@ -347,6 +356,13 @@ file name.  A name that contains \fB:\fR is considered to 
be a switch.
 Otherwise, it is a file if a file by that name exists, a switch if
 not.
 .IP
+With \fB\-\-tables\fR, \fBovs\-ofctl\fR only considers flows from the
+comma-separated table IDs specified, e.g. (\fB\-\-tables 1,3,4,2\fR).
+The valid range of table IDs is 0 to 254. Unlike \fBreplace\-flows\fR,
+the order of table IDs is ignored. If an ID is not specified, the table
+with that ID is ignored and is diffed. If \fB\-\-tables\fR is not
+specified, all tables are diffed.
+.IP
 For this command, an exit status of 0 means that no differences were
 found, 1 means that an error occurred, and 2 means that some
 differences were found.
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 3d61c4b..f3ff4ce 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+#include <assert.h>
 #include <config.h>
 #include <ctype.h>
 #include <errno.h>
@@ -29,6 +29,7 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 
+#include "bitmap.h"
 #include "byte-order.h"
 #include "classifier.h"
 #include "command-line.h"
@@ -37,6 +38,8 @@
 #include "dirs.h"
 #include "dynamic-string.h"
 #include "fatal-signal.h"
+#include "hmap.h"
+#include "list.h"
 #include "nx-match.h"
 #include "odp-util.h"
 #include "ofp-actions.h"
@@ -50,6 +53,7 @@
 #include "ofproto/ofproto.h"
 #include "openflow/nicira-ext.h"
 #include "openflow/openflow.h"
+#include "openflow/openflow-common.h"
 #include "dp-packet.h"
 #include "packets.h"
 #include "pcap-file.h"
@@ -67,6 +71,8 @@
 
 VLOG_DEFINE_THIS_MODULE(ofctl);
 
+#define TABLE_IDS_BITMAP_LEN (OFPTT_MAX + 1)
+
 /* --strict: Use strict matching for flow mod commands?  Additionally governs
  * use of nx_pull_match() instead of nx_pull_match_loose() in parse-nx-match.
  */
@@ -96,6 +102,14 @@ static bool timestamp;
      commands. */
 static char *unixctl_path;
 
+/* -T, --tables: Comma-separated list of OF flow table IDs to replace flows in.
+ *  tables is a ovs_list of table IDs specified in --tables. */
+static struct ovs_list *tables;
+
+/* bmtables is a bitmap where an offset is set if the corresponding table
+ * ID is specified in --tables. */
+static unsigned long *bmtables;
+
 /* --sort, --rsort: Sort order. */
 enum sort_order { SORT_ASC, SORT_DESC };
 struct sort_criterion {
@@ -114,6 +128,103 @@ static bool recv_flow_stats_reply(struct vconn *, 
ovs_be32 send_xid,
                                   struct ofpbuf **replyp,
                                   struct ofputil_flow_stats *,
                                   struct ofpbuf *ofpacts);
+
+int table_ids_list_and_bitmap_from_string(struct ovs_list **tables,
+                                       unsigned long **bmtables,
+                                       const char *s);
+
+bool print_diff_flows(struct classifier *cls);
+
+ /*
+ * An element in the list of table_ids. The list has table ids in the order
+ * specified in --tables passed to replace-flows.
+ */
+struct tables_node {
+    struct ovs_list lnode;
+    uint8_t table_id;
+};
+
+/* Holds the hmap_node with hash and the actual classifier. */
+struct hmap_table_classifier_node {
+    struct hmap_node hnode;
+    struct classifier cls;
+    uint8_t table_id;
+};
+
+/*
+ * Creates a classifier for table_id and adds it into the hmap. The table_id
+ * is used as the hash.
+ */
+static struct classifier *
+insert_new_table_classifier(struct hmap *ht_cls, uint8_t table_id)
+{
+    struct hmap_table_classifier_node *ht_cls_node;
+
+    ht_cls_node = xzalloc(sizeof(struct hmap_table_classifier_node));
+    ht_cls_node->table_id = table_id;
+    classifier_init(&ht_cls_node->cls, NULL);
+    /* Use the table_id as the hash. */
+    hmap_insert(ht_cls, &ht_cls_node->hnode, table_id);
+    return &ht_cls_node->cls;
+}
+
+/*
+ * Get the classifier for table_id. The table_id is used as the hash.
+ */
+static struct classifier *
+get_table_classifier(struct hmap *ht_cls, uint8_t table_id)
+{
+    struct hmap_table_classifier_node *ht_cls_node;
+    HMAP_FOR_EACH_IN_BUCKET(ht_cls_node, hnode, table_id, ht_cls)
+    {
+        if (ht_cls_node->table_id == table_id) {
+            return &ht_cls_node->cls;
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Takes a string of comma-separated table IDs, creates a list
+ * containing the table_ids in the order specified in the --tables argument.
+ * Also creates a bitmap of specified table IDs.
+ */
+int table_ids_list_and_bitmap_from_string(struct ovs_list **tables,
+                                       unsigned long **bmtables,
+                                       const char *s)
+{
+    char *stemp = xstrdup(s);
+    char *tok, *temp;
+    unsigned int table_id;
+    struct tables_node *tnode;
+
+    *tables = xzalloc(sizeof(struct ovs_list));
+    list_init(*tables);
+
+    *bmtables = bitmap_allocate(TABLE_IDS_BITMAP_LEN);
+
+    temp = stemp;
+    while(stemp) {
+        tok = strsep(&stemp, ",");
+        if(!str_to_uint(tok, 10, &table_id) || table_id > 
TABLE_IDS_BITMAP_LEN-1) {
+            LIST_FOR_EACH(tnode, lnode, *tables) {
+               free(tnode);
+            }
+            free(*tables);
+            bitmap_free(*bmtables);
+            free(temp);
+            return -1;
+        }
+        tnode = xzalloc(sizeof(struct tables_node));
+        tnode->table_id = table_id;
+        list_push_back(*tables, &tnode->lnode);
+
+        bitmap_set(*bmtables, table_id, true);
+    }
+    free(temp);
+    return 0;
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -164,6 +275,7 @@ parse_options(int argc, char *argv[])
         VLOG_OPTION_ENUMS
     };
     static const struct option long_options[] = {
+        {"tables", required_argument, NULL, 'T'},
         {"timeout", required_argument, NULL, 't'},
         {"strict", no_argument, NULL, OPT_STRICT},
         {"readd", no_argument, NULL, OPT_READD},
@@ -206,6 +318,7 @@ parse_options(int argc, char *argv[])
     for (;;) {
         unsigned long int timeout;
         int c;
+        int retval = 0;
 
         c = getopt_long(argc, argv, short_options, long_options, NULL);
         if (c == -1) {
@@ -223,6 +336,15 @@ parse_options(int argc, char *argv[])
             }
             break;
 
+        case 'T':
+            retval = table_ids_list_and_bitmap_from_string(&tables, &bmtables, 
optarg);
+            if (retval < 0) {
+                ovs_fatal(0, "one of the passed table IDs %s for "
+                        "-T or --tables is invalid", optarg);
+            }
+            break;
+
+
         case 'F':
             allowed_protocols = ofputil_protocols_from_string(optarg);
             if (!allowed_protocols) {
@@ -374,6 +496,7 @@ usage(void)
     ofp_version_usage();
     vlog_usage();
     printf("\nOther options:\n"
+           "  -T, --tables=TABLES         replace or diff flows in specified 
TABLES\n"
            "  --strict                    use strict match for flow commands\n"
            "  --readd                     replace flows that haven't changed\n"
            "  -F, --flow-format=FORMAT    force particular flow format\n"
@@ -2409,12 +2532,13 @@ fte_insert(struct classifier *cls, const struct match 
*match,
  * 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)
+read_flows_from_file(const char *filename, struct hmap *ht_cls, int index)
 {
     enum ofputil_protocol usable_protocols;
     int line_number;
     struct ds s;
     FILE *file;
+    struct classifier *cls;
 
     file = !strcmp(filename, "-") ? stdin : fopen(filename, "r");
     if (file == NULL) {
@@ -2424,7 +2548,7 @@ read_flows_from_file(const char *filename, struct 
classifier *cls, int index)
     ds_init(&s);
     usable_protocols = OFPUTIL_P_ANY;
     line_number = 0;
-    classifier_defer(cls);
+
     while (!ds_get_preprocessed_line(&s, file, &line_number)) {
         struct fte_version *version;
         struct ofputil_flow_mod fm;
@@ -2446,10 +2570,26 @@ read_flows_from_file(const char *filename, struct 
classifier *cls, int index)
                                      | OFPUTIL_FF_EMERG);
         version->ofpacts = fm.ofpacts;
         version->ofpacts_len = fm.ofpacts_len;
+        if (fm.table_id == 0xff) {
+            fm.table_id = 0x0;
+        }
 
+        if (tables && bmtables && !bitmap_is_set(bmtables, fm.table_id)) {
+            /*
+             * Ignore this flow if --tables is specified but does
+             * not contain the table_id.
+             */
+            continue;
+        }
+
+        cls = get_table_classifier(ht_cls, fm.table_id);
+        if (cls == NULL) {
+            cls = insert_new_table_classifier(ht_cls, fm.table_id);
+        }
+        classifier_defer(cls);
         fte_insert(cls, &fm.match, fm.priority, version, index);
+        classifier_publish(cls);
     }
-    classifier_publish(cls);
     ds_destroy(&s);
 
     if (file != stdin) {
@@ -2518,27 +2658,29 @@ recv_flow_stats_reply(struct vconn *vconn, ovs_be32 
send_xid,
 static void
 read_flows_from_switch(struct vconn *vconn,
                        enum ofputil_protocol protocol,
-                       struct classifier *cls, int index)
+                       struct hmap *ht_cls, int index)
 {
+    struct classifier *cls;
+    enum ofputil_protocol usable_protocols;
     struct ofputil_flow_stats_request fsr;
     struct ofputil_flow_stats fs;
     struct ofpbuf *request;
     struct ofpbuf ofpacts;
     struct ofpbuf *reply;
     ovs_be32 send_xid;
+    char *error;
+
+    error = parse_ofp_flow_stats_request_str(&fsr, false, "", 
&usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
 
-    fsr.aggregate = false;
-    match_init_catchall(&fsr.match);
-    fsr.out_port = OFPP_ANY;
-    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);
 
     reply = NULL;
     ofpbuf_init(&ofpacts, 0);
-    classifier_defer(cls);
     while (recv_flow_stats_reply(vconn, send_xid, &reply, &fs, &ofpacts)) {
         struct fte_version *version;
 
@@ -2550,35 +2692,48 @@ read_flows_from_switch(struct vconn *vconn,
         version->flags = 0;
         version->ofpacts_len = fs.ofpacts_len;
         version->ofpacts = xmemdup(fs.ofpacts, fs.ofpacts_len);
-
+        if (tables && bmtables && !bitmap_is_set(bmtables, fs.table_id)) {
+            /*
+             * Ignore this flow if --tables is specified but does
+             * not contain the table_id.
+             */
+            continue;
+        }
+        cls = get_table_classifier(ht_cls, fs.table_id);
+        if (cls == NULL) {
+            cls = insert_new_table_classifier(ht_cls, fs.table_id);
+        }
+        classifier_defer(cls);
         fte_insert(cls, &fs.match, fs.priority, version, index);
+        classifier_publish(cls);
     }
-    classifier_publish(cls);
     ofpbuf_uninit(&ofpacts);
 }
 
 static void
-fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
-                  enum ofputil_protocol protocol, struct ovs_list *packets)
+fte_make_flow_mod(const struct fte *fte,
+                  int index, uint16_t command,
+                  enum ofputil_protocol protocol,
+                  struct ovs_list *packets, uint8_t table_id)
 {
     const struct fte_version *version = fte->versions[index];
     struct ofputil_flow_mod fm;
     struct ofpbuf *ofm;
-
     minimatch_expand(&fte->rule.match, &fm.match);
     fm.priority = fte->rule.priority;
     fm.cookie = htonll(0);
     fm.cookie_mask = htonll(0);
     fm.new_cookie = version->cookie;
     fm.modify_cookie = true;
-    fm.table_id = 0xff;
+    fm.table_id = table_id;
     fm.command = command;
     fm.idle_timeout = version->idle_timeout;
     fm.hard_timeout = version->hard_timeout;
-    fm.importance = version->importance;
     fm.buffer_id = UINT32_MAX;
     fm.out_port = OFPP_ANY;
+    fm.out_group = OFPG_ANY;
     fm.flags = version->flags;
+    fm.importance = version->importance;
     if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
         command == OFPFC_MODIFY_STRICT) {
         fm.ofpacts = version->ofpacts;
@@ -2594,90 +2749,191 @@ fte_make_flow_mod(const struct fte *fte, int index, 
uint16_t command,
 }
 
 static void
+delete_extra_switch_flows(struct ovs_list *requests,
+                                      struct hmap *ht_cls,
+                                      enum ofputil_protocol protocol)
+{
+    enum { FILE_IDX = 0, SWITCH_IDX = 1 };
+    struct classifier *cls;
+    struct fte *fte;
+
+    if (tables) {
+        struct tables_node *tnode;
+        LIST_FOR_EACH(tnode, lnode, tables) {
+            cls = get_table_classifier(ht_cls, tnode->table_id);
+            if (cls == NULL) {
+                /* Trying to delete a flow that does not exist. */
+                continue;
+            }
+            CLS_FOR_EACH (fte, rule, cls) {
+                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,
+                                      tnode->table_id);
+                }
+            }
+        }
+    }
+    else {
+        struct hmap_table_classifier_node *ht_cls_node;
+        HMAP_FOR_EACH(ht_cls_node, hnode, ht_cls) {
+            cls = get_table_classifier(ht_cls, ht_cls_node->table_id);
+            if (cls == NULL) {
+                /* Trying to delete a flow that does not exist. */
+                continue;
+            }
+            CLS_FOR_EACH(fte, rule, cls) {
+                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,
+                                      ht_cls_node->table_id);
+                }
+            }
+        }
+    }
+}
+
+static void
+add_update_switch_flows(struct ovs_list *requests,
+                                    struct hmap *ht_cls,
+                                    enum ofputil_protocol protocol)
+{
+    enum { FILE_IDX = 0, SWITCH_IDX = 1 };
+    struct classifier *cls;
+    struct fte *fte;
+    if (tables) {
+        struct tables_node *tnode;
+        LIST_FOR_EACH(tnode, lnode, tables) {
+            cls = get_table_classifier(ht_cls, tnode->table_id);
+            if (cls == NULL) {
+                /* Trying to replace a flow that does not exist. */
+                continue;
+            }
+            CLS_FOR_EACH (fte, rule, cls) {
+                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_make_flow_mod(fte, FILE_IDX,
+                                      OFPFC_ADD,
+                                      protocol, requests,
+                                      tnode->table_id);
+                }
+                if (file_ver && sw_ver && !fte_version_equals(sw_ver, 
file_ver)) {
+                    fte_make_flow_mod(fte, FILE_IDX,
+                                      OFPFC_MODIFY_STRICT,
+                                      protocol, requests,
+                                      tnode->table_id);
+                }
+            }
+            fte_free_all(cls);
+        }
+    }
+    else {
+        struct hmap_table_classifier_node *ht_cls_node;
+        HMAP_FOR_EACH (ht_cls_node, hnode, ht_cls) {
+            cls = get_table_classifier(ht_cls, ht_cls_node->table_id);
+            if (cls == NULL) {
+                /* Trying to replace a flow that does not exist. */
+                continue;
+            }
+            CLS_FOR_EACH(fte, rule, cls) {
+                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_make_flow_mod(fte, FILE_IDX,
+                                      OFPFC_ADD,
+                                      protocol, requests,
+                                      ht_cls_node->table_id);
+                }
+                if (file_ver && sw_ver && !fte_version_equals(sw_ver, 
file_ver)) {
+                    fte_make_flow_mod(fte, FILE_IDX,
+                                      OFPFC_MODIFY_STRICT,
+                                      protocol, requests,
+                                      ht_cls_node->table_id);
+                }
+            }
+            fte_free_all(cls);
+        }
+    }
+}
+
+static void
 ofctl_replace_flows(struct ovs_cmdl_context *ctx)
 {
     enum { FILE_IDX = 0, SWITCH_IDX = 1 };
     enum ofputil_protocol usable_protocols, protocol;
-    struct classifier cls;
+
+    struct hmap ht_cls;
     struct ovs_list requests;
     struct vconn *vconn;
-    struct fte *fte;
 
-    classifier_init(&cls, NULL);
-    usable_protocols = read_flows_from_file(ctx->argv[2], &cls, FILE_IDX);
+    /* Initialize the hash map. */
+    hmap_init(&ht_cls);
 
-    protocol = open_vconn(ctx->argv[1], &vconn);
+    /* Setup connection to switch and generate dump request. */
+    usable_protocols = read_flows_from_file(ctx->argv[2], &ht_cls, FILE_IDX);
+    protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, usable_protocols);
     protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
-
-    read_flows_from_switch(vconn, protocol, &cls, SWITCH_IDX);
+    read_flows_from_switch(vconn, protocol, &ht_cls, SWITCH_IDX);
 
     list_init(&requests);
 
     /* Delete flows that exist on the switch but not in the file. */
-    CLS_FOR_EACH (fte, rule, &cls) {
-        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);
-        }
-    }
+    /* If --tables is specified, iterate through tables,
+     * perform deletion in order.
+     */
+    delete_extra_switch_flows(&requests, &ht_cls, protocol);
 
     /* Add flows that exist in the file but not on the switch.
      * Update flows that exist in both places but differ. */
-    CLS_FOR_EACH (fte, rule, &cls) {
-        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);
-        }
-    }
+    add_update_switch_flows(&requests, &ht_cls, protocol);
+ 
     transact_multiple_noreply(vconn, &requests);
+    hmap_destroy(&ht_cls);
     vconn_close(vconn);
-
-    fte_free_all(&cls);
 }
 
 static void
-read_flows_from_source(const char *source, struct classifier *cls, int index)
+read_flows_from_source(const char *source, struct hmap *ht_cls, int index)
 {
     struct stat s;
-
     if (source[0] == '/' || source[0] == '.'
         || (!strchr(source, ':') && !stat(source, &s))) {
-        read_flows_from_file(source, cls, index);
+        read_flows_from_file(source, ht_cls, index);
     } else {
         enum ofputil_protocol protocol;
         struct vconn *vconn;
 
+        /* Setup connection to switch and generate dump request. */ 
         protocol = open_vconn(source, &vconn);
         protocol = set_protocol_for_flow_dump(vconn, protocol, OFPUTIL_P_ANY);
-        read_flows_from_switch(vconn, protocol, cls, index);
+        read_flows_from_switch(vconn, protocol, ht_cls, index);
         vconn_close(vconn);
     }
 }
 
-static void
-ofctl_diff_flows(struct ovs_cmdl_context *ctx)
+bool
+print_diff_flows(struct classifier *cls)
 {
-    bool differences = false;
-    struct classifier cls;
     struct ds a_s, b_s;
     struct fte *fte;
-
-    classifier_init(&cls, NULL);
-    read_flows_from_source(ctx->argv[1], &cls, 0);
-    read_flows_from_source(ctx->argv[2], &cls, 1);
+    bool differences = false;
 
     ds_init(&a_s);
     ds_init(&b_s);
 
-    CLS_FOR_EACH (fte, rule, &cls) {
+    CLS_FOR_EACH(fte, rule, cls) {
         struct fte_version *a = fte->versions[0];
-        struct fte_version *b = fte->versions[1];
+        struct fte_version *b = fte->versions[1]; 
 
         if (!a || !b || !fte_version_equals(a, b)) {
             fte_version_format(fte, 0, &a_s);
@@ -2693,12 +2949,46 @@ ofctl_diff_flows(struct ovs_cmdl_context *ctx)
             }
         }
     }
-
+    fte_free_all(cls);
     ds_destroy(&a_s);
     ds_destroy(&b_s);
+    return differences;
+}
 
-    fte_free_all(&cls);
-
+static void
+ofctl_diff_flows(struct ovs_cmdl_context *ctx)
+{
+    bool differences = false;
+    struct classifier *cls;
+    struct hmap ht_cls;
+
+    hmap_init(&ht_cls);
+    read_flows_from_source(ctx->argv[1], &ht_cls, 0);
+    read_flows_from_source(ctx->argv[2], &ht_cls, 1);
+
+    if (tables) {
+        struct tables_node *tnode;
+        LIST_FOR_EACH (tnode, lnode, tables) {
+            cls = get_table_classifier(&ht_cls, tnode->table_id);
+            if (cls == NULL) {
+                /* Trying to diff a flow that does not exist. */
+                continue;
+            }
+            differences = print_diff_flows(cls);
+        }
+    }
+    else {
+        struct hmap_table_classifier_node *ht_cls_node;
+        HMAP_FOR_EACH (ht_cls_node, hnode, &ht_cls) {
+            cls = get_table_classifier(&ht_cls, ht_cls_node->table_id);
+            if (cls == NULL) {
+                /* Trying to diff a flow that does not exist. */
+                continue;
+            }
+            differences = print_diff_flows(cls);
+        }
+    }
+    hmap_destroy(&ht_cls);
     if (differences) {
         exit(2);
     }
-- 
1.8.5.2 (Apple Git-48)

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to