From patchwork Sun Oct 18 23:22:22 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shashank Shanbhag X-Patchwork-Id: 532064 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (li376-54.members.linode.com [96.126.127.54]) by ozlabs.org (Postfix) with ESMTP id E774C1402B0 for ; Mon, 19 Oct 2015 10:22:40 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=j5cemgrf; dkim-atps=neutral Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id D4DEE108E3; Sun, 18 Oct 2015 16:22:37 -0700 (PDT) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx3v1.cudamail.com (mx3.cudamail.com [64.34.241.5]) by archives.nicira.com (Postfix) with ESMTPS id AE189108E1 for ; Sun, 18 Oct 2015 16:22:36 -0700 (PDT) Received: from bar4.cudamail.com (bar2 [192.168.15.2]) by mx3v1.cudamail.com (Postfix) with ESMTP id 4C6AA6185CB for ; Sun, 18 Oct 2015 17:22:34 -0600 (MDT) X-ASG-Debug-ID: 1445210552-03dc210f8db10d0001-byXFYA Received: from mx3-pf3.cudamail.com ([192.168.14.3]) by bar4.cudamail.com with ESMTP id CfVVLDCoPlPgkIJz (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Sun, 18 Oct 2015 17:22:32 -0600 (MDT) X-Barracuda-Envelope-From: shashank.shanbhag@gmail.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.3 Received: from unknown (HELO mail-pa0-f41.google.com) (209.85.220.41) by mx3-pf3.cudamail.com with ESMTPS (RC4-SHA encrypted); 18 Oct 2015 23:22:26 -0000 Received-SPF: pass (mx3-pf3.cudamail.com: SPF record at _netblocks.google.com designates 209.85.220.41 as permitted sender) X-Barracuda-RBL-Trusted-Forwarder: 209.85.220.41 Received: by padhk11 with SMTP id hk11so10134284pad.1 for ; Sun, 18 Oct 2015 16:22:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id; bh=pHW0i3XodmEtlJZ2NvnQfVBvHaaRyO2IwKWQ6Z4waAM=; b=j5cemgrfatd5/eYK5qJNwuCBB+chC+8ROj7am6WdpHAJWPKpv9Y7y81YJaOEWXs5Rc 1cdQ+fGqlsV8fYgJAPsJaJUuREyrAm3eWZvpCGja7tuN3FSnRqFnZb21yJS+ZnChJ4ad ri0qIXoWb8y3JuOxwiVdXtze4XJGS9haEpIU7F2R8T+bzGcpkUPgHr0PrJyTtyznPVaF 5sf5Tmcb1/t0zpdeO4+4mlbRIiJWMl9yaiWmEHwqCsDv3I124Kde/cEHnQxgdSkyYS3i Ig+g//W0vA1ec/sZmO6DcZl7ufSIresjdGD6TveIhS/jT+Y73zJjO/jm8/kXCAXcgGbK NlzQ== X-Received: by 10.68.92.194 with SMTP id co2mr31147685pbb.147.1445210546224; Sun, 18 Oct 2015 16:22:26 -0700 (PDT) Received: from moria.montecristo.in (c-67-164-110-137.hsd1.ca.comcast.net. [67.164.110.137]) by smtp.gmail.com with ESMTPSA id ha1sm11009671pbc.54.2015.10.18.16.22.25 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 18 Oct 2015 16:22:25 -0700 (PDT) X-CudaMail-Envelope-Sender: shashank.shanbhag@gmail.com X-Barracuda-Apparent-Source-IP: 67.164.110.137 From: Shashank Shanbhag To: shashank.shanbhag@gmail.com, romain.lenglet@oracle.com, dev@openvswitch.org X-CudaMail-MID: CM-V3-1017018365 X-CudaMail-DTE: 101815 X-CudaMail-Originating-IP: 209.85.220.41 Date: Sun, 18 Oct 2015 16:22:22 -0700 X-ASG-Orig-Subj: [##CM-V3-1017018365##][PATCH] ovs-ofctl: replace-flows and diff-flows support for multiple tables Message-Id: <1445210542-15483-1-git-send-email-shashank.shanbhag@gmail.com> X-Mailer: git-send-email 1.8.5.2 (Apple Git-48) X-GBUdb-Analysis: 0, 209.85.220.41, Ugly c=0.372106 p=-0.459459 Source Normal X-MessageSniffer-Rules: 0-0-0-32767-c X-Barracuda-Connect: UNKNOWN[192.168.14.3] X-Barracuda-Start-Time: 1445210552 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-Barracuda-BRTS-Status: 1 X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-Spam-Score: 1.36 X-Barracuda-Spam-Status: No, SCORE=1.36 using per-user scores of TAG_LEVEL=3.0 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=3.0 tests=BSF_RULE_7580D, BSF_SC0_SA_TO_FROM_DOMAIN_MATCH, BSF_SC5_MJ1963, DKIM_SIGNED, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.23613 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.00 DKIM_SIGNED Domain Keys Identified Mail: message has a signature 0.75 BSF_RULE_7580D Custom Rule 7580D 0.10 RDNS_NONE Delivered to trusted network by a host with no rDNS 0.01 BSF_SC0_SA_TO_FROM_DOMAIN_MATCH Sender Domain Matches Recipient Domain 0.50 BSF_SC5_MJ1963 Custom Rule MJ1963 Subject: [ovs-dev] [PATCH] ovs-ofctl: replace-flows and diff-flows support for multiple tables X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" Fix replace-flows and diff-flows to modify/diff flows in multiple tables. Add a --tables(-T) option that allows the user to specify a comma-separated list of table indexes to replace/diff. Signed-off-by: Shashank Shanbhag Acked-by: Romain Lenglet Acked-by: Shashank Shanbhag Acked-by: Shashank Shanbhag --- AUTHORS | 1 + NEWS | 1 + tests/ovs-ofctl.at | 129 +++++++++---- utilities/ovs-ofctl.8.in | 20 +- utilities/ovs-ofctl.c | 467 ++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 523 insertions(+), 95 deletions(-) diff --git a/AUTHORS b/AUTHORS index f4e1ca9..ef8ae10 100644 --- a/AUTHORS +++ b/AUTHORS @@ -170,6 +170,7 @@ Scott Mann sdmnix@gmail.com Selvamuthukumar smkumar@merunetworks.com Shad Ansari shad.ansari@hpe.com Shan Wei davidshan@tencent.com +Shashank Shanbhag shashank.shanbhag@gmail.com Shih-Hao Li shli@nicira.com Shu Shen shu.shen@radisys.com Simon Horman horms@verge.net.au diff --git a/NEWS b/NEWS index 9b9dff2..ce0031c 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,7 @@ Post-v2.4.0 - Add support for connection tracking through the new "ct" action and "ct_state"/"ct_zone"/"ct_mark"/"ct_label" match fields. Only available on Linux kernels with the connection tracking module loaded. + - ovs-ofctl: replace-flows and diff-flows now operate on multiple tables. v2.4.0 - 20 Aug 2015 diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 33e67ed..a28da84 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -2838,27 +2838,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 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 del-flows br0]) +AT_CHECK([ovs-ofctl add-flows br0 add-flows.txt]) +AT_CHECK([ovs-ofctl -O OpenFlow12 replace-flows br0 oneflow.txt]) +AT_CHECK([ovs-ofctl -O OpenFlow12 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 del-flows br0]) +AT_CHECK([ovs-ofctl add-flows br0 add-flows.txt]) +AT_CHECK([ovs-ofctl -O OpenFlow12 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 OpenFlow12 dump-flows br0 | ofctl_strip | sed '/OFPST_FLOW/d' | sort], [0], [expout]) OVS_VSWITCHD_STOP AT_CLEANUP @@ -2870,14 +2925,14 @@ AT_CHECK([ovs-appctl vlog/set vconn:dbg]) dnl Add flows to br0 with importance via OF1.4+, using an OF1.4+ bundle. 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 -AT_CHECK([ovs-ofctl --bundle add-flows br0 add-flows.txt]) +AT_CHECK([ovs-ofctl -O OpenFlow14 --bundle 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 -AT_CHECK([ovs-ofctl --bundle replace-flows br0 replace-flows.txt]) +AT_CHECK([ovs-ofctl -O OpenFlow14 --bundle 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 +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]) @@ -2885,12 +2940,10 @@ dnl Check logs for OpenFlow trace # Prevent race. OVS_WAIT_UNTIL([cat ovs-vswitchd.log | vconn_windows_sub | test `grep -- "|vconn|DBG|unix: sent (Success): OFPST_FLOW reply" | wc -l` -ge 2]) # AT_CHECK([sed -n "s/^.*\(|vconn|DBG|.*xid=.*:\).*$/\1/p" ovs-vswitchd.log], [0], [dnl -AT_CHECK([print_vconn_debug | vconn_windows_sub | ofctl_strip], [0], [dnl +AT_CHECK([print_vconn_debug | vconn_windows_sub | ofctl_strip | awk 'NF && $1!~/^(table|active|version)/'], [0], [dnl vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): - version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.4): - version bitmap: 0x01, 0x05 -vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports versions 0x01, 0x05) +vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05) vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): @@ -2924,47 +2977,61 @@ vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): - version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.4): - version bitmap: 0x01, 0x05 -vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports versions 0x01, 0x05) +vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05) +vconn|DBG|unix: received: OFPST_TABLE request (OF1.4): +vconn|DBG|unix: sent (Success): OFPST_TABLE reply (OF1.4): vconn|DBG|unix: received: OFPST_FLOW request (OF1.4): vconn|DBG|unix: sent (Success): OFPST_FLOW reply (OF1.4): + importance=1, dl_vlan=1 actions=drop + importance=2, dl_vlan=2 actions=drop + importance=3, dl_vlan=3 actions=drop + importance=4, dl_vlan=4 actions=drop + importance=5, dl_vlan=5 actions=drop + importance=6, dl_vlan=6 actions=drop + importance=7, dl_vlan=7 actions=drop + importance=8, dl_vlan=8 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=OPEN_REPLY flags=0 vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered -OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=1 importance:11 actions=drop +OFPT_FLOW_MOD (OF1.4): MOD_STRICT dl_vlan=1 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered -OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=3 importance:13 actions=drop +OFPT_FLOW_MOD (OF1.4): MOD_STRICT dl_vlan=3 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered -OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=5 importance:15 actions=drop +OFPT_FLOW_MOD (OF1.4): MOD_STRICT dl_vlan=5 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): bundle_id=0 flags=atomic ordered -OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=7 importance:17 actions=drop +OFPT_FLOW_MOD (OF1.4): MOD_STRICT dl_vlan=7 actions=drop +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): + bundle_id=0 flags=atomic ordered +OFPT_FLOW_MOD (OF1.4): DEL_STRICT dl_vlan=2 actions=drop +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): + bundle_id=0 flags=atomic ordered +OFPT_FLOW_MOD (OF1.4): DEL_STRICT dl_vlan=4 actions=drop +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): + bundle_id=0 flags=atomic ordered +OFPT_FLOW_MOD (OF1.4): DEL_STRICT dl_vlan=6 actions=drop +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4): + bundle_id=0 flags=atomic ordered +OFPT_FLOW_MOD (OF1.4): DEL_STRICT dl_vlan=8 actions=drop vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4): bundle_id=0 type=COMMIT_REPLY flags=0 vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5): - version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 vconn|DBG|unix: received: OFPT_HELLO (OF1.4): - version bitmap: 0x05 vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05) vconn|DBG|unix: received: OFPST_FLOW request (OF1.4): vconn|DBG|unix: sent (Success): OFPST_FLOW reply (OF1.4): - importance=2, dl_vlan=2 actions=drop - importance=4, dl_vlan=4 actions=drop - importance=6, dl_vlan=6 actions=drop - importance=8, dl_vlan=8 actions=drop - importance=11, dl_vlan=1 actions=drop - importance=13, dl_vlan=3 actions=drop - importance=15, dl_vlan=5 actions=drop - importance=17, dl_vlan=7 actions=drop + importance=1, dl_vlan=1 actions=drop + importance=3, dl_vlan=3 actions=drop + importance=5, dl_vlan=5 actions=drop + importance=7, dl_vlan=7 actions=drop ]) OVS_VSWITCHD_STOP diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index a6087f6..c041748 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -340,7 +340,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\-\-bundle\fR] [\fB\-\-readd\fR] \fBreplace\-flows \fIswitch file\fR" +.IP "[\fB\-\-bundle\fR] [\fB\-\-readd\fR] [\fB\-\-tables \fltable-ids\fR] \fBreplace\-flows \fIswitch file\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 @@ -349,12 +349,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 visible tables, +as reported by the switch, 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 "[\fB\-\-tables \fltable-ids\fR] \fBdiff\-flows \fIsource1 source2\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 @@ -369,6 +378,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 visible tables, as reported by the switch, 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 0c315c1..7d8357f 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -29,6 +29,7 @@ #include #include +#include "bitmap.h" #include "byte-order.h" #include "classifier.h" #include "command-line.h" @@ -37,6 +38,7 @@ #include "dirs.h" #include "dynamic-string.h" #include "fatal-signal.h" +#include "list.h" #include "nx-match.h" #include "odp-util.h" #include "ofp-actions.h" @@ -50,6 +52,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 +70,9 @@ VLOG_DEFINE_THIS_MODULE(ofctl); +#define TABLE_IDS_BITMAP_LEN (OFPTT_MAX + 1) +#define NUMBER_OF_TABLES (OFPTT_MAX + 1) + /* --bundle: Use OpenFlow 1.4 bundle for making the flow table change atomic. * NOTE: Also the flow mod will use OpenFlow 1.4, so the semantics may be * different (see the comment in parse_options() for details). @@ -102,6 +108,27 @@ static bool timestamp; commands. */ static char *unixctl_path; +/* -T, --tables: Comma-separated list of OF flow table IDs to replace flows in. + * tables is an array that has the table IDs specified in order. + */ +static uint8_t tables[NUMBER_OF_TABLES]; +static int n_tables; + +/* An array of pointers to classifiers - one for each table. */ +static struct classifier *classifiers[NUMBER_OF_TABLES]; + +/* A bitmap where an offset is set if the corresponding table + * ID needs to be operated on by replace-flows or diff-flows command. If a + * table ID is hidden, the corresponding offset is unset even if specified + * in -T, --tables. + */ +static unsigned long *bmtables; + +/* A bitmap where an offset is set if the corresponding table + * ID is not hidden. + */ +static unsigned long *visible_tables; + /* --sort, --rsort: Sort order. */ enum sort_order { SORT_ASC, SORT_DESC }; struct sort_criterion { @@ -120,6 +147,200 @@ static bool recv_flow_stats_reply(struct vconn *, ovs_be32 send_xid, struct ofpbuf **replyp, struct ofputil_flow_stats *, struct ofpbuf *ofpacts); + +static void perform_stats_transaction(struct vconn *, + struct ofpbuf *request, + bool dump); + +static bool tables_and_bitmap_from_string(const char *); + +static void update_bitmaps_with_supported_table_ids(struct vconn *); + +static void populate_visible_tables_bitmap(struct vconn *, + const struct ofp_header *); + +static bool print_diff_flows(struct classifier *); + +static void fte_free_all(struct classifier *); + +/* Initializes the array of classifiers. */ +static void +initialize_classifiers(void) +{ + int table_id; + + for (table_id=0; table_id < NUMBER_OF_TABLES; table_id++) { + classifiers[table_id] = NULL; + } +} + +/* Creates a classifier for table with ID 'table_id' and adds it to the + * array of classifiers. Returns the '*classifier'. + */ +static struct classifier * +insert_new_classifier(uint8_t table_id) +{ + classifiers[table_id] = xzalloc(sizeof(struct classifier)); + classifier_init(classifiers[table_id], NULL); + return classifiers[table_id]; +} + +/* Returns the '*classifier' for the table with ID 'table_id'. */ +static struct classifier * +get_classifier(uint8_t table_id) +{ + return classifiers[table_id]; +} + +/* Frees memory allocated to classifiers. Also, destroys all flows + * within those classifiers. + */ +static void +destroy_classifiers(void) +{ + int table_id; + + for (table_id=0; table_id < NUMBER_OF_TABLES; table_id++) { + if (classifiers[table_id] != NULL) { + fte_free_all(classifiers[table_id]); + free(classifiers[table_id]); + } + } +} + +/* + * Takes a string '*s' of comma-separated table IDs and populates the array of + * table IDs in the order specified in the --tables argument. Also, creates + * a bitmap of specified table IDs. + * + * Returns true if successful, otherwise false. + */ +static bool +tables_and_bitmap_from_string(const char *s) +{ + char *stemp = xstrdup(s); + char *tok, *temp; + unsigned int table_id; + n_tables = 0; + + 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) { + bitmap_free(bmtables); + free(temp); + return false; + } + tables[n_tables++] = table_id; + bitmap_set(bmtables, table_id, true); + } + free(temp); + + return true; +} + +/* + * Makes sure a bmtables bit is set only if the corresponding + * table ID is supported (not hidden) in the switch. The connection '*vconn' + * is used to get the supported table IDs. + */ +static void +update_bitmaps_with_supported_table_ids(struct vconn *vconn) +{ + struct ofpbuf *request; + + visible_tables = bitmap_allocate(TABLE_IDS_BITMAP_LEN); + + if (n_tables == 0) { + /* Initialize the bitmap bmtables and set all bits. */ + bmtables = bitmap_allocate1(TABLE_IDS_BITMAP_LEN); + } + + request = ofpraw_alloc(OFPRAW_OFPST_TABLE_REQUEST, + vconn_get_version(vconn), 0); + perform_stats_transaction(vconn, request, false); + bitmap_and(bmtables, visible_tables, TABLE_IDS_BITMAP_LEN); +} + +/* + * Uses connection '*vconn' to get all visible table IDs. Sets offsets in + * visible_tables bitmap that correspond to the table IDs. + */ +static void +populate_visible_tables_bitmap(struct vconn *vconn, + const struct ofp_header *oh) +{ + struct ofpbuf msg; + ofpbuf_use_const(&msg, oh, ntohs(oh->length)); + ofpraw_pull_assert(&msg); + + for (;;) { + struct ofputil_table_features features; + struct ofputil_table_stats stats; + int retval; + + retval = ofputil_decode_table_stats_reply(&msg, &stats, &features); + if (retval) { + if (retval != EOF) { + ovs_fatal(0, "%s: Error while decoding table features: %s", + vconn_get_name(vconn), ofperr_get_name(retval)); + } + return; + } + bitmap_set(visible_tables, features.table_id, true); + } +} + +/* + * Returns the classifier for a given 'table_id'. Creates the classifier if + * there is no classifier for the given 'table_id'. + */ +static struct classifier * +return_classifier_for_table(uint8_t table_id) +{ + struct classifier *cls; + + if (n_tables > 0 + && bmtables + && !bitmap_is_set(bmtables, table_id)) { + /* + * Ignore this flow if --tables is specified but does + * not contain the table_id. + */ + return NULL; + } + cls = get_classifier(table_id); + if (cls == NULL) { + cls = insert_new_classifier(table_id); + } + return cls; +} + +/* + * Returns the classifier for a given 'index'. If the order of tables has been + * specified using --tables, return classifier indexed by 'index' in the + * 'tables' array. + */ +static struct classifier * +return_classifier_and_current_tableid(int *id, int index) +{ + struct classifier *cls; + + if (n_tables == 0) { + cls = get_classifier(index); + *id = index; + } else { + /* Since --tables has been specified, get classifier for the table + * indexed by 'index' in 'tables' array. */ + cls = get_classifier(tables[index]); + *id = tables[index]; + } + return cls; +} + int main(int argc, char *argv[]) { @@ -173,6 +394,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}, @@ -216,6 +438,7 @@ parse_options(int argc, char *argv[]) for (;;) { unsigned long int timeout; int c; + bool retval = 0; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { @@ -233,6 +456,14 @@ parse_options(int argc, char *argv[]) } break; + case 'T': + retval = tables_and_bitmap_from_string(optarg); + if (!retval) { + 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) { @@ -401,6 +632,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" @@ -549,8 +781,15 @@ dump_trivial_transaction(const char *vconn_name, enum ofpraw raw) vconn_close(vconn); } +/* + * Sends the stats 'request' using the connection 'vconn'. If 'dump' is true, + * prints the stats response to stdout. If 'dump' is false, gets the list of + * supported tables, and updates 'bmtables'. + */ static void -dump_stats_transaction(struct vconn *vconn, struct ofpbuf *request) +perform_stats_transaction(struct vconn *vconn, + struct ofpbuf *request, + bool dump) { const struct ofp_header *request_oh = request->data; ovs_be32 send_xid = request_oh->xid; @@ -571,9 +810,14 @@ dump_stats_transaction(struct vconn *vconn, struct ofpbuf *request) recv_xid = ((struct ofp_header *) reply->data)->xid; if (send_xid == recv_xid) { enum ofpraw raw; - - ofp_print(stdout, reply->data, reply->size, verbosity + 1); - + if (dump) { + ofp_print(stdout, reply->data, reply->size, verbosity + 1); + } + else { + /* Update bmtables - set table ID bit in bmtables. */ + populate_visible_tables_bitmap(vconn, + (struct ofp_header *) reply->data); + } ofpraw_decode(&raw, reply->data); if (ofptype_from_ofpraw(raw) == OFPTYPE_ERROR) { done = true; @@ -593,6 +837,12 @@ dump_stats_transaction(struct vconn *vconn, struct ofpbuf *request) } static void +dump_stats_transaction(struct vconn *vconn, struct ofpbuf *request) +{ + perform_stats_transaction(vconn, request, true); +} + +static void dump_trivial_stats_transaction(const char *vconn_name, enum ofpraw raw) { struct ofpbuf *request; @@ -2585,6 +2835,7 @@ fte_insert(struct classifier *cls, const struct match *match, int priority, struct fte_version *version, int index) { struct fte *old, *fte; + classifier_defer(cls); fte = xzalloc(sizeof *fte); cls_rule_init(&fte->rule, match, priority); @@ -2604,12 +2855,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, 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) { @@ -2619,7 +2871,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; @@ -2641,10 +2893,18 @@ 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; + } + + cls = return_classifier_for_table(fm.table_id); + if (cls == NULL) { + continue; + } fte_insert(cls, &fm.match, fm.priority, version, index); } - classifier_publish(cls); + ds_destroy(&s); if (file != stdin) { @@ -2713,8 +2973,9 @@ 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) + int index) { + struct classifier *cls; struct ofputil_flow_stats_request fsr; struct ofputil_flow_stats fs; struct ofpbuf *request; @@ -2724,16 +2985,29 @@ read_flows_from_switch(struct vconn *vconn, fsr.aggregate = false; match_init_catchall(&fsr.match); - fsr.out_port = OFPP_ANY; - fsr.table_id = 0xff; + + /* Only get tables that are not hidden. */ + update_bitmaps_with_supported_table_ids(vconn); + + if (n_tables == 1) { + /* Only retrieve flows from the specified table. */ + fsr.table_id = tables[0]; + } + else { + fsr.table_id = 0xff; + } + fsr.cookie = fsr.cookie_mask = htonll(0); + fsr.out_group = OFPG11_ANY; + fsr.out_port = OFPP_ANY; + 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; @@ -2746,34 +3020,40 @@ read_flows_from_switch(struct vconn *vconn, version->ofpacts_len = fs.ofpacts_len; version->ofpacts = xmemdup(fs.ofpacts, fs.ofpacts_len); + cls = return_classifier_for_table(fs.table_id); + if (cls == NULL) { + continue; + } + fte_insert(cls, &fs.match, fs.priority, version, index); } - 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; @@ -2789,92 +3069,133 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command, } static void +construct_flow_mods(struct ovs_list *requests, + enum ofputil_protocol protocol, + struct fte *fte, + uint8_t table_id) +{ + enum { FILE_IDX = 0, SWITCH_IDX = 1 }; + 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, + table_id); + } + else if (file_ver + && (readd || !sw_ver)) { + fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, + protocol, requests, + table_id); + } + else if (file_ver + && sw_ver && !fte_version_equals(sw_ver, file_ver)) { + fte_make_flow_mod(fte, FILE_IDX, + OFPFC_MODIFY_STRICT, + protocol, requests, + table_id); + } +} + +static void +add_update_delete_switch_flows(struct ovs_list *requests, + enum ofputil_protocol protocol) +{ + struct classifier *cls; + struct fte *fte; + int table_id; + /* current_id can be either table_id or tables[table_id]. */ + int current_id = 0; + + int num_tables = n_tables > 0 ? n_tables: NUMBER_OF_TABLES; + + for (table_id=0; table_id < num_tables; table_id++) { + cls = return_classifier_and_current_tableid(¤t_id, table_id); + if (cls == NULL) { + continue; + } + classifier_publish(cls); + CLS_FOR_EACH(fte, rule, cls) { + construct_flow_mods(requests, + protocol, + fte, + current_id); + } + } +} + +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 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 array of classifiers. */ + initialize_classifiers(); - protocol = open_vconn(ctx->argv[1], &vconn); + /* Read flows from the file. */ + usable_protocols = read_flows_from_file(ctx->argv[2], FILE_IDX); + + /* Setup connection to the switch. */ + 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); + /* Generate switch dump request. */ + read_flows_from_switch(vconn, protocol, 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); - } - } - - /* 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]; + /* Delete flows that exist on the switch but not in the file. Add flows + * that exist in the file but not on the switch. Update the flows that + * exist in both places but differ. If --tables is specified, iterate + * through tables, perform deletion in order. + */ + add_update_delete_switch_flows(&requests, protocol); - if (file_ver - && (readd || !sw_ver || !fte_version_equals(sw_ver, file_ver))) { - fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests); - } - } if (bundle) { bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); } else { transact_multiple_noreply(vconn, &requests); } + destroy_classifiers(); 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, 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, 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, index); vconn_close(vconn); } } -static void -ofctl_diff_flows(struct ovs_cmdl_context *ctx) +static 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) { + classifier_publish(cls); + CLS_FOR_EACH (fte, rule, cls) { struct fte_version *a = fte->versions[0]; struct fte_version *b = fte->versions[1]; @@ -2892,12 +3213,34 @@ ofctl_diff_flows(struct ovs_cmdl_context *ctx) } } } - ds_destroy(&a_s); ds_destroy(&b_s); + return differences; +} + +static void +ofctl_diff_flows(struct ovs_cmdl_context *ctx) +{ + bool differences = false; + struct classifier *cls; + int current_id = 0; + int table_id; - fte_free_all(&cls); + initialize_classifiers(); + read_flows_from_source(ctx->argv[1], 0); + read_flows_from_source(ctx->argv[2], 1); + + int num_tables = n_tables > 0 ? n_tables : NUMBER_OF_TABLES; + + for (table_id=0; table_id < num_tables; table_id++) { + cls = return_classifier_and_current_tableid(¤t_id, table_id); + if (cls == NULL) { + continue; + } + differences = print_diff_flows(cls); + } + destroy_classifiers(); if (differences) { exit(2); }