Break dpctl into two programs: ovs-ofctl and ovs-dpctl.
authorBen Pfaff <blp@nicira.com>
Fri, 15 May 2009 22:48:58 +0000 (15:48 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 15 May 2009 23:30:27 +0000 (16:30 -0700)
The datapath and OpenFlow are fairly different and it seems wrong
conceptually to work with both in a single program.  So this commit breaks
them up into two programs.

28 files changed:
INSTALL
README
debian/openvswitch-switch.init
debian/openvswitch-switch.install
debian/openvswitch-switch.manpages
debian/openvswitch-switch.template
debian/ovs-switch-setup.8
lib/vlog-modules.def
secchan/secchan.8.in
utilities/.gitignore
utilities/automake.mk
utilities/dpctl.8.in [deleted file]
utilities/dpctl.c [deleted file]
utilities/ovs-appctl.8.in
utilities/ovs-controller.8.in
utilities/ovs-discover.8.in
utilities/ovs-dpctl.8.in [new file with mode: 0644]
utilities/ovs-dpctl.c [new file with mode: 0644]
utilities/ovs-monitor
utilities/ovs-ofctl.8.in [new file with mode: 0644]
utilities/ovs-ofctl.c [new file with mode: 0644]
utilities/ovs-pki.8.in
vswitchd/bridge.c
vswitchd/vswitchd.8.in
vswitchd/vswitchd.conf.5.in
xenserver/etc_init.d_vswitch
xenserver/etc_profile.d_vswitch.sh
xenserver/vswitch-xen.spec

diff --git a/INSTALL b/INSTALL
index dbab025f93aad064209a9606e825fab858da66fb..5aa7073e89ff1cb45a32058e8112b8d7232155e8 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -150,7 +150,7 @@ distribution in the ordinary way using "configure" and "make".
 
       - Bridge compatibility daemon: vswitchd/brcompatd
 
-      - Datapath administration utility: utilities/dpctl.
+      - Datapath administration utility: utilities/ovs-dpctl.
 
    Some less important binaries will be built also:
 
@@ -321,10 +321,10 @@ The OpenVSwitch kernel module must be loaded, as described under
    program such as "su" to become root temporarily.
 
 1. Create a datapath instance.  The command below creates a datapath
-   identified as dp0 (see dpctl(8) for more detailed usage
+   identified as dp0 (see ovs-dpctl(8) for more detailed usage
    information).
 
-      # dpctl adddp dp0
+      # ovs-dpctl adddp dp0
    
    (dp0 is the first datapath within a host.  openvswitch_mod supports
    multiple datapaths within the same host, which would be identified
@@ -335,18 +335,18 @@ The OpenVSwitch kernel module must be loaded, as described under
    bridged to the physical switch ports by the secchan, for use in
    in-band control.
 
-2. Use dpctl to attach the datapath to physical interfaces on the
+2. Use ovs-dpctl to attach the datapath to physical interfaces on the
    machine.  Say, for example, you want to create a trivial 2-port
-   switch using interfaces eth1 and eth2, you would issue the following
-   commands:
+   switch using interfaces eth1 and eth2, you would issue the
+   following commands:
 
-      # dpctl addif dp0 eth1
-      # dpctl addif dp0 eth2
+      # ovs-dpctl addif dp0 eth1
+      # ovs-dpctl addif dp0 eth2
 
    You can verify that the interfaces were successfully added by asking
-   dpctl to print the current status of datapath dp0:
+   ovs-dpctl to print the current status of datapath dp0:
 
-      # dpctl show dp0
+      # ovs-dpctl show dp0
 
 3. Arrange so that the switch can reach the controller over the
    network.
diff --git a/README b/README
index e4a7c7149b49a7911b1498fd8f4d9fe7eef20414..737029a1adf5dc83a0b7557b5329b41b51d7661c 100644 (file)
--- a/README
+++ b/README
@@ -28,7 +28,7 @@ The most important components of this distribution are:
 
         - vswitchd, a daemon that implements the virtual switch.
 
-       - dpctl, a tool for configuring the kernel module and
+       - ovs-dpctl, a tool for configuring the kernel module and
           controlling OpenFlow switches.
 
 This distribution includes some additional software as well:
@@ -39,6 +39,9 @@ This distribution includes some additional software as well:
 
        - ovs-controller, a simple OpenFlow switch
 
+       - ovs-ofctl, a utility for querying and controlling OpenFlow
+          switches and controllers.
+
        - vlog-appctl, a utility that can control OpenVSwitch daemons,
           adjusting their logging levels among other uses.
 
index 45a87d25d493419af8c144417c70a98e82841368..3f96c9147148a3335e4b855bbecd1a8eec68611c 100755 (executable)
@@ -265,7 +265,7 @@ case "$1" in
             check_op "Removing IP address from $netdev" ifconfig $netdev 0.0.0.0
         done
 
-        must_succeed "Creating datapath" dpctl adddp of0 $NETDEVS
+        must_succeed "Creating datapath" ovs-dpctl adddp of0 $NETDEVS
 
         xx='[0-9abcdefABCDEF][0-9abcdefABCDEF]'
         case $DATAPATH_ID in
@@ -385,7 +385,7 @@ case "$1" in
            --exec $DAEMON
        echo "$NAME."
 
-        check_op "Deleting datapath" dpctl deldp of0
+        check_op "Deleting datapath" ovs-dpctl deldp of0
         check_op "Unloading kernel module" modprobe -r openvswitch_mod
        ;;
     force-stop)
index a43c9c60d7d1894ee39df679b96715c6b16e2d45..9fddacf016c371555795b3236a22d32cd0dbbb3f 100644 (file)
@@ -1,6 +1,7 @@
 _debian/secchan/secchan usr/sbin
-_debian/utilities/dpctl usr/sbin
+_debian/utilities/ovs-dpctl usr/sbin
 _debian/utilities/ovs-discover usr/sbin
 _debian/utilities/ovs-kill usr/sbin
+_debian/utilities/ovs-ofctl usr/sbin
 debian/openvswitch/usr/share/openvswitch/commands/* usr/share/openvswitch/commands
 debian/commands/* usr/share/openvswitch/commands
index 80bfd9fd6f645d8247608ffa200877c84c8878f7..f789eba9f7950780a0d99813a8349c6206a205c2 100644 (file)
@@ -1,4 +1,5 @@
 _debian/secchan/secchan.8
 _debian/utilities/ovs-discover.8
+_debian/utilities/ovs-dpctl.8
 _debian/utilities/ovs-kill.8
-_debian/utilities/dpctl.8
+_debian/utilities/ovs-ofctl.8
index 704a3bbd0e17cb826c3e36e46e2f0e15813d283d..739475bc181df5d62600edc83060754e7601ca8e 100644 (file)
@@ -102,7 +102,7 @@ SWITCH_IP=dhcp
 #CACERT_MODE=secure
 
 # MGMT_VCONNS: List of vconns (space-separated) on which secchan
-# should listen for management connections from dpctl, etc.
+# should listen for management connections from ovs-ofctl, etc.
 # openvswitch-switchui by default connects to
 # unix:/var/run/secchan.mgmt, so do not disable this if you want to
 # use openvswitch-switchui.
index f369587c65e1d012a1e1768a28de8a882274307f..e34656d70553253c012f88ae58ad47eca854d1f7 100644 (file)
@@ -36,6 +36,6 @@ obtained from the OpenFlow PKI server.
 
 .SH "SEE ALSO"
 
+.BR ovs\-dpctl (8),
 .BR ovs-pki (8),
-.BR dpctl (8),
 .BR secchan (8)
index e9221e0d402c56d6fbece5b9aa077d6e2f783e8a..4ef458ab61ceb94c68a8d36e9f75b757fe3bdb77 100644 (file)
@@ -29,6 +29,7 @@ VLOG_MODULE(mgmt)
 VLOG_MODULE(netdev)
 VLOG_MODULE(netflow)
 VLOG_MODULE(netlink)
+VLOG_MODULE(ofctl)
 VLOG_MODULE(ovs_discover)
 VLOG_MODULE(ofproto)
 VLOG_MODULE(pktbuf)
index e456c530470a19525cf2aafcfd6a4113c5954440..8a8429479aecc0f71415ed67df781d68159d96d2 100644 (file)
@@ -50,7 +50,7 @@ over the network.  It can do so in one of two ways:
 .IP out-of-band
 In this configuration, OpenFlow traffic uses a network separate from
 the data traffic that it controls, that is, the switch does not use
-any of the network devices added to the datapath with \fBdpctl
+any of the network devices added to the datapath with \fBovs\-dpctl
 addif\fR in its communication with the controller.
 
 To use \fBsecchan\fR in a network with out-of-band control, specify
@@ -61,7 +61,7 @@ is started.
 .IP in-band
 In this configuration, a single network is used for OpenFlow traffic
 and other data traffic, that is, the switch contacts the controller
-over one of the network devices added to the datapath with \fBdpctl
+over one of the network devices added to the datapath with \fBovs\-dpctl
 addif\fR.  This configuration is often more convenient than
 out-of-band control, because it is not necessary to maintain two
 independent networks.
@@ -80,7 +80,7 @@ automatically, do not specify the location of the controller on the
 
 In this mode, \fBsecchan\fR will broadcast a DHCP request with vendor
 class identifier \fBOpenFlow\fR across the network devices added to
-the datapath with \fBdpctl addif\fR.  It will accept any valid DHCP
+the datapath with \fBovs\-dpctl addif\fR.  It will accept any valid DHCP
 reply that has the same vendor class identifier and includes a
 vendor-specific option with code 1 whose contents are a string
 specifying the location of the controller in the same format used on
@@ -149,7 +149,7 @@ argument.  You must also configure the network device for the OpenFlow
 ``local port'' to allow \fBsecchan\fR to connect to that controller.
 The OpenFlow local port is a virtual network port that \fBsecchan\fR
 bridges to the physical switch ports.  The name of the local port for
-a given \fIdatapath\fR may be seen by running \fBdpctl dp-show
+a given \fIdatapath\fR may be seen by running \fBovs\-dpctl dp-show
 \fIdatapath\fR; the local port is listed as port 0 in \fBdp-show\fR's
 output.
 
@@ -300,7 +300,7 @@ time is 15 seconds.
 .TP
 \fB-l\fR, \fB--listen=\fImethod\fR
 Configures the switch to additionally listen for incoming OpenFlow
-connections for switch management with \fBdpctl\fR.  The \fImethod\fR
+connections for switch management with \fBovs\-ofctl\fR.  The \fImethod\fR
 must be given as one of the passive OpenFlow connection methods listed
 below.  This option may be specified multiple times to listen to
 multiple connection methods.
@@ -329,7 +329,7 @@ be given as one of the passive OpenFlow connection methods listed
 under the \fB--listen\fR option above.  This option may be specified
 multiple times to listen to multiple connection methods.
 
-If \fBdpctl monitor\fR is used to connect to \fImethod\fR specified on
+If \fBovs\-ofctl monitor\fR is used to connect to \fImethod\fR specified on
 \fB--snoop\fR, it will display all the OpenFlow messages traveling
 between the switch and its controller on the primary OpenFlow
 connection.  This can be useful for debugging switch and controller
@@ -382,7 +382,7 @@ This option takes effect only when \fB--rate-limit\fR is also specified.
 .TP
 \fB--command-acl=\fR[\fB!\fR]\fIglob\fR[\fB,\fR[\fB!\fR]\fIglob\fR...]
 Configures the commands that remote OpenFlow connections are allowed
-to invoke using (e.g.) \fBdpctl execute\fR.  The argument is a
+to invoke using (e.g.) \fBovs\-ofctl execute\fR.  The argument is a
 comma-separated sequence of shell glob patterns.  A glob pattern
 specified without a leading \fB!\fR is a ``whitelist'' that specifies
 a set of commands that are that may be invoked, whereas a pattern that
@@ -454,9 +454,10 @@ require the controller to send the CA certificate, but
 
 .SH "SEE ALSO"
 
-.BR dpctl (8),
 .BR ovs\-appctl (8),
-.BR ovs-discover (8),
 .BR ovs\-controller (8),
-.BR ovs-pki (8),
+.BR ovs\-discover (8),
+.BR ovs\-dpctl (8),
+.BR ovs\-ofctl (8),
+.BR ovs\-pki (8),
 .BR vswitchd.conf (5)
index 7cfc5c49624ec59645e3645bc880ee8bf7eb96d2..32a7f2eb7249e33513a9c5eaacf6d1e0997c346f 100644 (file)
@@ -1,7 +1,5 @@
 /Makefile
 /Makefile.in
-/dpctl
-/dpctl.8
 /nlmon
 /ovs-appctl
 /ovs-appctl.8
@@ -11,8 +9,12 @@
 /ovs-controller.8
 /ovs-discover
 /ovs-discover.8
+/ovs-dpctl
+/ovs-dpctl.8
 /ovs-kill
 /ovs-kill.8
+/ovs-ofctl
+/ovs-ofctl.8
 /ovs-parse-leaks
 /ovs-pki
 /ovs-pki-cgi
index 35c303c34692435f5aea5b5179ad0cf2bd654bb3..97b827acc89dd8407a24b012c0cce8af0d71d70e 100644 (file)
@@ -1,10 +1,11 @@
 bin_PROGRAMS += \
-       utilities/dpctl \
        utilities/ovs-appctl \
        utilities/ovs-cfg-mod \
        utilities/ovs-controller \
        utilities/ovs-discover \
+       utilities/ovs-dpctl \
        utilities/ovs-kill \
+       utilities/ovs-ofctl \
        utilities/ovs-wdt
 noinst_PROGRAMS += utilities/nlmon
 bin_SCRIPTS += utilities/ovs-pki
@@ -12,40 +13,40 @@ noinst_SCRIPTS += utilities/ovs-pki-cgi utilities/ovs-parse-leaks
 dist_sbin_SCRIPTS += utilities/ovs-monitor 
 
 EXTRA_DIST += \
-       utilities/dpctl.8.in \
        utilities/ovs-appctl.8.in \
        utilities/ovs-cfg-mod.8.in \
        utilities/ovs-controller.8.in \
        utilities/ovs-discover.8.in \
+       utilities/ovs-dpctl.8.in \
        utilities/ovs-kill.8.in \
+       utilities/ovs-ofctl.8.in \
        utilities/ovs-parse-leaks.in \
        utilities/ovs-pki-cgi.in \
        utilities/ovs-pki.8.in \
        utilities/ovs-pki.in
 DISTCLEANFILES += \
-       utilities/dpctl.8 \
        utilities/ovs-appctl.8 \
        utilities/ovs-cfg-mod.8 \
        utilities/ovs-controller.8 \
        utilities/ovs-discover.8 \
+       utilities/ovs-dpctl.8 \
        utilities/ovs-kill.8 \
+       utilities/ovs-ofctl.8 \
        utilities/ovs-parse-leaks \
        utilities/ovs-pki \
        utilities/ovs-pki.8 \
        utilities/ovs-pki-cgi
 
 man_MANS += \
-       utilities/dpctl.8 \
        utilities/ovs-appctl.8 \
        utilities/ovs-cfg-mod.8 \
        utilities/ovs-controller.8 \
        utilities/ovs-discover.8 \
+       utilities/ovs-dpctl.8 \
        utilities/ovs-kill.8 \
+       utilities/ovs-ofctl.8 \
        utilities/ovs-pki.8
 
-utilities_dpctl_SOURCES = utilities/dpctl.c
-utilities_dpctl_LDADD = lib/libopenvswitch.a $(FAULT_LIBS) $(SSL_LIBS)
-
 utilities_ovs_appctl_SOURCES = utilities/ovs-appctl.c
 utilities_ovs_appctl_LDADD = lib/libopenvswitch.a
 
@@ -58,9 +59,15 @@ utilities_ovs_controller_LDADD = lib/libopenvswitch.a $(FAULT_LIBS) $(SSL_LIBS)
 utilities_ovs_discover_SOURCES = utilities/ovs-discover.c
 utilities_ovs_discover_LDADD = lib/libopenvswitch.a
 
+utilities_ovs_dpctl_SOURCES = utilities/ovs-dpctl.c
+utilities_ovs_dpctl_LDADD = lib/libopenvswitch.a $(FAULT_LIBS)
+
 utilities_ovs_kill_SOURCES = utilities/ovs-kill.c
 utilities_ovs_kill_LDADD = lib/libopenvswitch.a
 
+utilities_ovs_ofctl_SOURCES = utilities/ovs-ofctl.c
+utilities_ovs_ofctl_LDADD = lib/libopenvswitch.a $(FAULT_LIBS) $(SSL_LIBS)
+
 utilities_ovs_wdt_SOURCES = utilities/ovs-wdt.c
 
 utilities_nlmon_SOURCES = utilities/nlmon.c
diff --git a/utilities/dpctl.8.in b/utilities/dpctl.8.in
deleted file mode 100644 (file)
index c88d3d3..0000000
+++ /dev/null
@@ -1,645 +0,0 @@
-.TH dpctl 8 "March 2009" "OpenVSwitch" "OpenVSwitch Manual"
-.ds PN dpctl
-
-.SH NAME
-dpctl \- administer OpenVSwitch datapaths and OpenFlow switches
-
-.SH SYNOPSIS
-.B dpctl
-[\fIoptions\fR] \fIcommand \fR[\fIswitch\fR] [\fIargs\fR\&...]
-
-.SH DESCRIPTION
-The
-.B dpctl
-program is a command line tool for monitoring and administering
-OpenVSwitch datapaths and OpenFlow switches.
-.B dpctl
-can add, delete, and modify OpenVSwitch datapaths.  It can also show
-the current state of an OpenFlow switch, including features,
-configuration, and table entries.
-
-.SS "OpenVSwitch Datapath Commands"
-
-The \fBdpctl\fR program can create, modify, and delete OpenVSwitch
-datapaths.  A single machine may host up to 256 datapaths (numbered 0
-to 255).
-
-A newly created datapath is associated with only one network device, a
-virtual network device sometimes called the datapath's ``local port''.
-A newly created datapath is not, however, associated with any of the
-host's other network devices.  To intercept and process traffic on a
-given network device, use the \fBaddif\fR command to explicitly add
-that network device to the datapath.
-
-Do not use \fBdpctl\fR commands to modify datapaths in conjunction
-with \fBvswitchd\fR(8).  Instead, modify the \fBvswitchd\fR
-configuration file and send \fBSIGHUP\fR to the \fBvswitchd\fR
-process.
-
-.PP
-Most \fBdpctl\fR commands that work with datapaths take an argument
-that specifies the name of the datapath, in one of the following
-forms:
-
-.so lib/dpif.man
-
-.PP
-The following commands manage datapaths.
-
-.TP
-\fBadddp \fIdp\fR [\fInetdev\fR...]
-
-Creates datapath \fIdp\fR.  The name of the new datapath's local port
-depends on how \fIdp\fR is specified: if it takes the form
-\fBdp\fIN\fR, the local port will be named \fBdp\fIN\fR; if \fIdp\fR
-is \fBnl:\fI, the local port will be named \fBof\fIN\fR; otherwise,
-the local port's name will be \fIdp\fR.
-
-This will fail if the host already has 256 datapaths, if a network
-device with the same name as the new datapath's local port already
-exists, or if \fIdp\fR is given in the form \fBdp\fIN\fR or
-\fBnl:\fIN\fR and a datapath numbered \fIN\fR already exists.
-
-If \fInetdev\fRs are specified, \fBdpctl\fR adds them to the datapath.
-
-.TP
-\fBdeldp \fIdp\fR
-Deletes datapath \fIdp\fR.  If \fIdp\fR is associated with any network
-devices, they are automatically removed.
-
-.TP
-\fBaddif \fIdp netdev\fR[\fIoption\fR...]...
-Adds each \fInetdev\fR to the set of network devices datapath
-\fIdp\fR monitors, where \fIdp\fR is the name of an existing
-datapath, and \fInetdev\fR is the name of one of the host's
-network devices, e.g. \fBeth0\fR.  Once a network device has been added
-to a datapath, the datapath has complete ownership of the network device's
-traffic and the network device appears silent to the rest of the
-system.
-
-A \fInetdev\fR may be followed by a comma-separated list of options.
-The following options are currently supported:
-
-.RS
-.IP "\fBport=\fIportno\fR"
-Specifies \fIportno\fR (a number between 1 and 255) as the port number
-at which \fInetdev\fR will be attached.  By default, \fBaddif\fR
-automatically selects the lowest available port number.
-
-.IP "\fBinternal\fR"
-Instead of attaching an existing \fInetdev\fR, creates an internal
-port (analogous to the local port) with that name.
-.RE
-
-.TP
-\fBdelif \fIdp netdev\fR...
-Removes each \fInetdev\fR from the list of network devices datapath
-\fIdp\fR monitors.
-
-.TP
-\fBget-idx \fIdp\fR
-Prints the datapath number of datapath \fIdp\fR.
-
-.TP
-\fBget-name \fIdp\fR
-Prints the name of datapath \fIdp\fR, that is, the name of the network
-device acting as its local port.
-
-.TP
-\fBdp-show \fR[\fIdp\fR...]
-Prints a summary of configured datapaths, including their datapath
-numbers and a list of ports connected to each datapath.  (The local
-port is identified as port 0.)
-
-If one or more datapaths are specified, information on only those
-datapaths are displayed.  Otherwise, \fBdpctl\fR displays information
-about all configured datapaths.
-
-.IP "\fBdp-dump-flows \fIdp\fR"
-Prints to the console all flow entries in datapath \fIdp\fR's
-flow table.
-
-This command is primarily useful for debugging OpenVSwitch.  The flow
-table entries that it displays are not
-OpenFlow flow entries.  Instead, they are different and considerably
-simpler flows maintained by the OpenVSwitch kernel module.
-
-.IP "\fBdp-del-flows \fIdp\fR"
-Deletes all flow entries from datapath \fIdp\fR's flow table.
-
-This command is primarily useful for debugging OpenVSwitch.  As
-discussed in \fBdp-dump-flows\fR, these entries are
-not OpenFlow flow entries.  By deleting them, the process that set them
-up may be confused about their disappearance.
-
-.IP "\fBdp-dump-groups \fIdp\fR"
-Prints to the console the sets of port groups maintained by datapath
-\fIdp\fR.  Ordinarily there are at least 2 port groups in a datapath
-that \fBsecchan\fR or \fBvswitch\fR is controlling: group 0 contains
-all ports except those disabled by STP, and group 1 contains all
-ports.  Additional groups might be used in the future.
-
-This command is primarily useful for debugging OpenVSwitch.  OpenFlow
-does not have a concept of port groups.
-
-.SS "OpenFlow Switch Management Commands"
-
-These commands allow \fBdpctl\fR to monitor and administer an OpenFlow
-switch.  It is able to show the current state of a switch, including
-features, configuration, and table entries.
-
-Most of these commands take an argument that specifies the method for
-connecting to an OpenFlow switch.  The following connection methods
-are supported:
-
-.RS
-.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.  The \fB--private-key\fR, \fB--certificate\fR, and
-\fB--ca-cert\fR options are mandatory when this form is used.
-
-.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
-
-.TP
-\fBunix:\fIfile\fR
-The Unix domain server socket named \fIfile\fR.  
-
-.IP "\fIfile\fR"
-This is short for \fBunix:\fIfile\fR, as long as \fIfile\fR does not
-contain a colon.
-
-.IP \fIdp\fR
-This is short for \fBunix:@RUNDIR@/\fIdp\fB.mgmt\fR, as long as
-\fIdp\fR does not contain a colon.
-.RE
-
-.TP
-\fBshow \fIswitch\fR
-Prints to the console information on \fIswitch\fR, including
-information on its flow tables and ports.
-
-.TP
-\fBstatus \fIswitch\fR [\fIkey\fR]
-Prints to the console a series of key-value pairs that report the
-status of \fIswitch\fR.  If \fIkey\fR is specified, only the key-value
-pairs whose key names begin with \fIkey\fR are printed.  If \fIkey\fR is
-omitted, all key-value pairs are printed.
-
-.TP
-\fBdump-tables \fIswitch\fR
-Prints to the console statistics for each of the flow tables used by
-\fIswitch\fR.
-
-.TP
-\fBdump-ports \fIswitch\fR
-Prints to the console statistics for each of the network devices
-associated with \fIswitch\fR.
-
-.TP
-\fBmod-port \fIswitch\fR \fInetdev\fR \fIaction\fR
-Modify characteristics of an interface monitored by \fIswitch\fR.  
-\fInetdev\fR can be referred to by its OpenFlow assigned port number or 
-the device name, e.g. \fBeth0\fR.  The \fIaction\fR may be any one of the
-following:
-
-.RS
-.IP \fBup\fR
-Enables the interface.  This is equivalent to ``ifconfig up'' on a Unix
-system.
-
-.IP \fBdown\fR
-Disables the interface.  This is equivalent to ``ifconfig down'' on a Unix
-system.
-
-.IP \fBflood\fR
-When a \fIflood\fR action is specified, traffic will be sent out this
-interface.  This is the default posture for monitored ports.
-
-.IP \fBnoflood\fR
-When a \fIflood\fR action is specified, traffic will not be sent out 
-this interface.  This is primarily useful to prevent loops when a
-spanning tree protocol is not in use.
-
-.RE
-
-.TP
-\fBdump-flows \fIswitch \fR[\fIflows\fR]
-Prints to the console all flow entries in \fIswitch\fR's
-tables that match \fIflows\fR.  If \fIflows\fR is omitted, all flows
-in the switch are retrieved.  See \fBFlow Syntax\fR, below, for the
-syntax of \fIflows\fR.  The output format is described in 
-\fBTable Entry Output\fR.
-
-.TP
-\fBdump-aggregate \fIswitch \fR[\fIflows\fR]
-Prints to the console aggregate statistics for flows in 
-\fIswitch\fR's tables that match \fIflows\fR.  If \fIflows\fR is omitted, 
-the statistics are aggregated across all flows in the switch's flow
-tables.  See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR.
-The output format is descrbed in \fBTable Entry Output\fR.
-
-.TP
-\fBadd-flow \fIswitch flow\fR
-Add the flow entry as described by \fIflow\fR to the \fIswitch\fR's 
-tables.  The flow entry is in the format described in \fBFlow Syntax\fR, 
-below.
-
-.TP
-\fBadd-flows \fIswitch file\fR
-Add flow entries as described in \fIfile\fR to \fIswitch\fR's 
-tables.  Each line in \fIfile\fR is a flow entry in the format
-described in \fBFlow Syntax\fR, below.
-
-.TP
-\fBmod-flows \fIswitch flow\fR
-Modify the actions in entries from the \fIswitch\fR's tables 
-that match \fIflow\fR.  When invoked with the \fB--strict\fR option,
-wildcards are not treated as active for matching purposes.  See 
-\fBFlow Syntax\fR, below, for the syntax of \fIflows\fR.
-
-.TP
-\fBdel-flows \fIswitch \fR[\fIflow\fR]
-Deletes entries from the \fIswitch\fR's tables that match
-\fIflow\fR.  When invoked with the \fB--strict\fR option, wildcards are 
-not treated as active for matching purposes.  If \fIflow\fR is 
-omitted and the \fB--strict\fR option is not used, all flows in the 
-switch's tables are removed.  See \fBFlow Syntax\fR, below, for the 
-syntax of \fIflows\fR.
-
-.TP
-\fBmonitor \fIswitch\fR [\fImiss-len\fR [\fIsend-exp]]
-Connects to \fIswitch\fR and prints to the console all OpenFlow
-messages received.  Usually, \fIswitch\fR should specify a connection
-named on \fBsecchan\fR(8)'s \fB-l\fR or \fB--listen\fR command line
-option.
-
-If \fImiss-len\fR is provided, \fBdpctl\fR sends an OpenFlow ``set
-configuration'' message at connection setup time that requests
-\fImiss-len\fR bytes of each packet that misses the flow table.  The
-OpenFlow reference implementation not send these messages to the
-\fBdpctl monitor\fR client connection unless a nonzero value is
-specified on this argument.
-
-If \fIsend-exp\fR is specified as \fB1\fR, \fBdpctl\fR will also
-request to be sent flow expiration messages.  If this argument is
-omitted, or \fB0\fR is specified, then \fRdpctl\fR will not request
-flow expirations.
-
-This command may be useful for debugging switch or controller
-implementations.
-
-.TP
-\fBexecute \fIswitch command \fR[\fIarg\fR...]
-Sends a request to \fIswitch\fR to execute \fIcommand\fR along with
-each \fIarg\fR, if any, then waits for the command to complete and
-reports its completion status on \fBstderr\fR and its output, if any,
-on \fBstdout\fR.  The set of available commands and their argument is
-switch-dependent.  (This command uses a Nicira extension to OpenFlow
-that may not be available on all switches.)
-
-.SS "OpenFlow Switch and Controller Commands"
-
-The following commands, like those in the previous section, may be
-applied to OpenFlow switches, using any of the connection methods
-described in that section.  Unlike those commands, these may also be
-applied to OpenFlow controllers.
-
-.TP
-\fBprobe \fItarget\fR
-Sends a single OpenFlow echo-request message to \fItarget\fR and waits
-for the response.  With the \fB-t\fR or \fB--timeout\fR option, this
-command can test whether an OpenFlow switch or controller is up and
-running.
-
-.TP
-\fBping \fItarget \fR[\fIn\fR]
-Sends a series of 10 echo request packets to \fItarget\fR and times
-each reply.  The echo request packets consist of an OpenFlow header
-plus \fIn\fR bytes (default: 64) of randomly generated payload.  This
-measures the latency of individual requests.
-
-.TP
-\fBbenchmark \fItarget n count\fR
-Sends \fIcount\fR echo request packets that each consist of an
-OpenFlow header plus \fIn\fR bytes of payload and waits for each
-response.  Reports the total time required.  This is a measure of the
-maximum bandwidth to \fItarget\fR for round-trips of \fIn\fR-byte
-messages.
-
-.SS "Flow Syntax"
-
-Some \fBdpctl\fR commands accept an argument that describes a flow or
-flows.  Such flow descriptions comprise a series
-\fIfield\fB=\fIvalue\fR assignments, separated by commas or white
-space.  (Embedding spaces into a flow description normally requires
-quoting to prevent the shell from breaking the description into
-multiple arguments.)
-
-The following field assignments describe how a flow matches a packet.
-If any of these assignments is omitted from the flow syntax, the field
-is treated as a wildcard; thus, if all of them are omitted, the
-resulting flow matches all packets.  The string \fB*\fR or \fBANY\fR
-may be specified to explicitly mark any of these fields as a wildcard.  
-(\fB*\fR should be quoted to protect it from shell expansion.)
-
-.IP \fBin_port=\fIport_no\fR
-Matches physical port \fIport_no\fR.  Switch ports are numbered as
-displayed by \fBdpctl show\fR.
-
-.IP \fBdl_vlan=\fIvlan\fR
-Matches IEEE 802.1q virtual LAN tag \fIvlan\fR.  Specify \fB0xffff\fR
-as \fIvlan\fR to match packets that are not tagged with a virtual LAN;
-otherwise, specify a number between 0 and 4095, inclusive, as the
-12-bit VLAN ID to match.
-
-.IP \fBdl_src=\fImac\fR
-Matches Ethernet source address \fImac\fR, which is specified as 6 pairs 
-of hexadecimal digits delimited by colons (e.g. \fB00:0A:E4:25:6B:B0\fR).
-
-.IP \fBdl_dst=\fImac\fR
-Matches Ethernet destination address \fImac\fR.
-
-.IP \fBdl_type=\fIethertype\fR
-Matches Ethernet protocol type \fIethertype\fR, which is specified as an
-integer between 0 and 65535, inclusive, either in decimal or as a 
-hexadecimal number prefixed by \fB0x\fR (e.g. \fB0x0806\fR to match ARP 
-packets).
-
-.IP \fBnw_src=\fIip\fR[\fB/\fInetmask\fR]
-Matches IPv4 source address \fIip\fR, which may be specified as an
-IP address or host name (e.g. \fB192.168.1.1\fR or
-\fBwww.example.com\fR).  The optional \fInetmask\fR allows restricting a
-match to an IPv4 address prefix.  The netmask may be specified as a dotted 
-quad (e.g. \fB192.168.1.0/255.255.255.0\fR) or as a CIDR block 
-(e.g. \fB192.168.1.0/24\fR).
-
-.IP \fBnw_dst=\fIip\fR[\fB/\fInetmask\fR]
-Matches IPv4 destination address \fIip\fR.
-
-.IP \fBnw_proto=\fIproto\fR
-Matches IP protocol type \fIproto\fR, which is specified as a decimal 
-number between 0 and 255, inclusive (e.g. 6 to match TCP packets).
-
-.IP \fBtp_src=\fIport\fR
-Matches UDP or TCP source port \fIport\fR, which is specified as a decimal 
-number between 0 and 65535, inclusive (e.g. 80 to match packets originating 
-from a HTTP server).
-
-.IP \fBtp_dst=\fIport\fR
-Matches UDP or TCP destination port \fIport\fR.
-
-.IP \fBicmp_type=\fItype\fR
-Matches ICMP message with \fItype\fR, which is specified as a decimal 
-number between 0 and 255, inclusive.
-
-.IP \fBicmp_code=\fIcode\fR
-Matches ICMP messages with \fIcode\fR.
-
-.PP
-The following shorthand notations are also available:
-
-.IP \fBip\fR
-Same as \fBdl_type=0x0800\fR.
-
-.IP \fBicmp\fR
-Same as \fBdl_type=0x0800,nw_proto=1\fR.
-
-.IP \fBtcp\fR
-Same as \fBdl_type=0x0800,nw_proto=6\fR.
-
-.IP \fBudp\fR
-Same as \fBdl_type=0x0800,nw_proto=17\fR.
-
-.IP \fBarp\fR
-Same as \fBdl_type=0x0806\fR.
-
-.PP
-The \fBadd-flow\fR and \fBadd-flows\fR commands require an additional field:
-
-.IP \fBactions=\fR[\fItarget\fR][\fB,\fItarget\fR...]\fR
-Specifies a comma-separated list of actions to take on a packet when the 
-flow entry matches.  If no \fItarget\fR is specified, then packets
-matching the flow are dropped.  The \fItarget\fR may be a decimal port 
-number designating the physical port on which to output the packet, or one 
-of the following keywords:
-
-.RS
-.IP \fBoutput\fR:\fIport\fR
-Outputs the packet on the port specified by \fIport\fR.
-
-.IP \fBnormal\fR
-Subjects the packet to the device's normal L2/L3 processing.  (This
-action is not implemented by all OpenFlow switches.)
-
-.IP \fBflood\fR
-Outputs the packet on all switch physical ports other than the port on
-which it was received and any ports on which flooding is disabled
-(typically, these would be ports disabled by the IEEE 802.1D spanning
-tree protocol).
-
-.IP \fBall\fR
-Outputs the packet on all switch physical ports other than the port on
-which it was received.
-
-.IP \fBcontroller\fR:\fImax_len\fR
-Sends the packet to the OpenFlow controller as a ``packet in''
-message.  If \fImax_len\fR is a number, then it specifies the maximum
-number of bytes that should be sent.  If \fImax_len\fR is \fBALL\fR or
-omitted, then the entire packet is sent.
-
-.IP \fBlocal\fR
-Outputs the packet on the ``local port,'' which corresponds to the
-\fBof\fIn\fR network device (see \fBCONTACTING THE CONTROLLER\fR in
-\fBsecchan\fR(8) for information on the \fBof\fIn\fR network device).
-
-.IP \fBdrop\fR
-Discards the packet, so no further processing or forwarding takes place.
-If a drop action is used, no other actions may be specified.
-
-.IP \fBmod_vlan_vid\fR:\fIvlan_vid\fR
-Modifies the VLAN id on a packet.  The VLAN tag is added or modified 
-as necessary to match the value specified.  If the VLAN tag is added,
-a priority of zero is used (see the \fBmod_vlan_pcp\fR action to set
-this).
-
-.IP \fBmod_vlan_pcp\fR:\fIvlan_pcp\fR
-Modifies the VLAN priority on a packet.  The VLAN tag is added or modified 
-as necessary to match the value specified.  Valid values are between 0
-(lowest) and 7 (highest).  If the VLAN tag is added, a vid of zero is used 
-(see the \fBmod_vlan_vid\fR action to set this).
-
-.IP \fBstrip_vlan\fR
-Strips the VLAN tag from a packet if it is present.
-
-.IP \fBmod_dl_src\fB:\fImac\fR
-Sets the source Ethernet address to \fImac\fR.
-
-.IP \fBmod_dl_dst\fB:\fImac\fR
-Sets the destination Ethernet address to \fImac\fR.
-.RE
-
-.IP
-(The OpenFlow protocol supports other actions that \fBdpctl\fR does
-not yet expose to the user.)
-
-.PP
-The \fBadd-flow\fR, \fBadd-flows\fR, and \fBdel-flows\fR commands
-support an additional optional field:
-
-.IP \fBpriority=\fIvalue\fR
-The priority at which a wildcarded entry will match in comparison to
-others.  \fIvalue\fR is a number between 0 and 65535, inclusive.  A higher 
-\fIvalue\fR will match before a lower one.  An exact-match entry will always 
-have priority over an entry containing wildcards, so it has an implicit 
-priority value of 65535.  When adding a flow, if the field is not specified, 
-the flow's priority will default to 32768.
-
-.PP
-The \fBadd-flow\fR and \fBadd-flows\fR commands support additional
-optional fields:
-
-.TP
-\fBidle_timeout=\fIseconds\fR
-Causes the flow to expire after the given number of seconds of
-inactivity.  A value of 0 prevents a flow from expiring due to
-inactivity.  The default is 60 seconds.
-
-.IP \fBhard_timeout=\fIseconds\fR
-Causes the flow to expire after the given number of seconds,
-regardless of activity.  A value of 0 (the default) gives the flow no
-hard expiration deadline.
-
-.PP
-The \fBdump-flows\fR, \fBdump-aggregate\fR, \fBdel-flow\fR 
-and \fBdel-flows\fR commands support one additional optional field:
-
-.TP
-\fBout_port=\fIport\fR
-If set, a matching flow must include an output action to \fIport\fR.
-
-.PP
-The \fBdump-flows\fR and \fBdump-aggregate\fR commands support an
-additional optional field:
-
-.IP \fBtable=\fInumber\fR
-If specified, limits the flows about which statistics are gathered to
-those in the table with the given \fInumber\fR.  Tables are numbered
-as shown by the \fBdump-tables\fR command.
-
-If this field is not specified, or if \fInumber\fR is given as
-\fB255\fR, statistics are gathered about flows from all tables.
-
-.SS "Table Entry Output"
-
-The \fBdump-tables\fR and \fBdump-aggregate\fR commands print information 
-about the entries in a datapath's tables.  Each line of output is a 
-unique flow entry, which begins with some common information:
-
-.IP \fBduration\fR
-The number of seconds the entry has been in the table.
-
-.IP \fBtable_id\fR
-The table that contains the flow.  When a packet arrives, the switch 
-begins searching for an entry at the lowest numbered table.  Tables are 
-numbered as shown by the \fBdump-tables\fR command.
-
-.IP \fBpriority\fR
-The priority of the entry in relation to other entries within the same
-table.  A higher value will match before a lower one.
-
-.IP \fBn_packets\fR
-The number of packets that have matched the entry.
-
-.IP \fBn_bytes\fR
-The total number of bytes from packets that have matched the entry.
-
-.PP
-The rest of the line consists of a description of the flow entry as 
-described in \fBFlow Syntax\fR, above.
-
-
-.SH OPTIONS
-.TP
-\fB--strict\fR
-Uses strict matching when running flow modification commands.
-
-.TP
-\fB-t\fR, \fB--timeout=\fIsecs\fR
-Limits \fBdpctl\fR runtime to approximately \fIsecs\fR seconds.  If
-the timeout expires, \fBdpctl\fR will exit with a \fBSIGALRM\fR
-signal.
-
-.TP
-\fB-p\fR, \fB--private-key=\fIprivkey.pem\fR
-Specifies a PEM file containing the private key used as the
-identity for SSL connections to a switch.
-
-.TP
-\fB-c\fR, \fB--certificate=\fIcert.pem\fR
-Specifies a PEM file containing a certificate, signed by the
-controller's certificate authority (CA), that certifies the
-private key to identify a trustworthy controller.
-
-.TP
-\fB-C\fR, \fB--ca-cert=\fIcacert.pem\fR
-Specifies a PEM file containing the CA certificate used to verify that
-a switch is trustworthy.
-
-.so lib/vlog.man
-.so lib/common.man
-
-.SH EXAMPLES
-
-.SS "Datapath Examples"
-
-A typical \fBdpctl\fR command sequence for controlling an OpenFlow
-kernel module:
-
-.TP
-\fBdpctl adddp dp0\fR
-Creates datapath numbered 0.
-
-.TP
-\fBdpctl addif dp0 eth0 eth1\fR
-Adds two network devices to the new datapath.
-
-.PP
-At this point one would ordinarily start \fBsecchan\fR(8) on
-\fBdp0\fR, transforming \fBdp0\fR into an OpenFlow switch.  Then, when
-the switch and the datapath is no longer needed:
-
-.TP
-\fBdpctl delif dp0 eth0 eth1\fR
-Removes network devices from the datapath.
-
-.TP
-\fBdpctl deldp dp0\fR
-Deletes the datapath.
-
-.SS "OpenFlow Switch Examples"
-
-The following examples assume that an OpenFlow switch on the local
-host has been configured to listen for management connections on a
-Unix domain socket named \fB@RUNDIR@/openflow.sock\fR, e.g. by
-specifying \fB--listen=punix:@RUNDIR@/openflow.sock\fR on the
-\fBsecchan\fR(8) command line.
-
-.TP
-\fBdpctl dump-tables unix:@RUNDIR@/openflow.sock\fR
-Prints out the switch's table stats.  (This is more interesting after
-some traffic has passed through.)
-
-.TP
-\fBdpctl dump-flows unix:@RUNDIR@/openflow.sock\fR
-Prints the flow entries in the switch.
-
-.SH "SEE ALSO"
-
-.BR vswitchd (8),
-.BR secchan (8),
-.BR ovs\-appctl (8),
-.BR ovs\-controller (8)
diff --git a/utilities/dpctl.c b/utilities/dpctl.c
deleted file mode 100644 (file)
index 15ea488..0000000
+++ /dev/null
@@ -1,1758 +0,0 @@
-/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
- * Junior University
- * 
- * We are making the OpenFlow specification and associated documentation
- * (Software) available for public use and benefit with the expectation
- * that others will use, modify and enhance the Software and contribute
- * those enhancements back to the community. However, since we would
- * like to make the Software available for broadest use, with as few
- * restrictions as possible permission is hereby granted, free of
- * charge, to any person obtaining a copy of this Software to deal in
- * the Software under the copyrights without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- * 
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- * 
- * The name and trademarks of copyright holder(s) may NOT be used in
- * advertising or publicity pertaining to the Software or any
- * derivatives without specific, written prior permission.
- */
-
-#include <config.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include "command-line.h"
-#include "compiler.h"
-#include "dirs.h"
-#include "dpif.h"
-#include "dynamic-string.h"
-#include "netdev.h"
-#include "netlink.h"
-#include "odp-util.h"
-#include "ofp-print.h"
-#include "ofpbuf.h"
-#include "openflow/nicira-ext.h"
-#include "openflow/openflow.h"
-#include "packets.h"
-#include "random.h"
-#include "socket-util.h"
-#include "timeval.h"
-#include "util.h"
-#include "vconn-ssl.h"
-#include "vconn.h"
-
-#include "vlog.h"
-#define THIS_MODULE VLM_dpctl
-
-#define DEFAULT_IDLE_TIMEOUT 60
-
-#define MOD_PORT_CMD_UP      "up"
-#define MOD_PORT_CMD_DOWN    "down"
-#define MOD_PORT_CMD_FLOOD   "flood"
-#define MOD_PORT_CMD_NOFLOOD "noflood"
-
-
-/* Settings that may be configured by the user. */
-struct settings {
-    bool strict;        /* Use strict matching for flow mod commands */
-};
-
-struct command {
-    const char *name;
-    int min_args;
-    int max_args;
-    void (*handler)(const struct settings *, int argc, char *argv[]);
-};
-
-static struct command all_commands[];
-
-static void usage(void) NO_RETURN;
-static void parse_options(int argc, char *argv[], struct settings *);
-
-int main(int argc, char *argv[])
-{
-    struct settings s;
-    struct command *p;
-
-    set_program_name(argv[0]);
-    time_init();
-    vlog_init();
-    parse_options(argc, argv, &s);
-    signal(SIGPIPE, SIG_IGN);
-
-    argc -= optind;
-    argv += optind;
-    if (argc < 1)
-        ovs_fatal(0, "missing command name; use --help for help");
-
-    for (p = all_commands; p->name != NULL; p++) {
-        if (!strcmp(p->name, argv[0])) {
-            int n_arg = argc - 1;
-            if (n_arg < p->min_args)
-                ovs_fatal(0, "'%s' command requires at least %d arguments",
-                          p->name, p->min_args);
-            else if (n_arg > p->max_args)
-                ovs_fatal(0, "'%s' command takes at most %d arguments",
-                          p->name, p->max_args);
-            else {
-                p->handler(&s, argc, argv);
-                if (ferror(stdout)) {
-                    ovs_fatal(0, "write to stdout failed");
-                }
-                if (ferror(stderr)) {
-                    ovs_fatal(0, "write to stderr failed");
-                }
-                exit(0);
-            }
-        }
-    }
-    ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]);
-
-    return 0;
-}
-
-static void
-parse_options(int argc, char *argv[], struct settings *s)
-{
-    enum {
-        OPT_STRICT = UCHAR_MAX + 1
-    };
-    static struct option long_options[] = {
-        {"timeout", required_argument, 0, 't'},
-        {"verbose", optional_argument, 0, 'v'},
-        {"strict", no_argument, 0, OPT_STRICT},
-        {"help", no_argument, 0, 'h'},
-        {"version", no_argument, 0, 'V'},
-        VCONN_SSL_LONG_OPTIONS
-        {0, 0, 0, 0},
-    };
-    char *short_options = long_options_to_short_options(long_options);
-
-    /* Set defaults that we can figure out before parsing options. */
-    s->strict = false;
-
-    for (;;) {
-        unsigned long int timeout;
-        int c;
-
-        c = getopt_long(argc, argv, short_options, long_options, NULL);
-        if (c == -1) {
-            break;
-        }
-
-        switch (c) {
-        case 't':
-            timeout = strtoul(optarg, NULL, 10);
-            if (timeout <= 0) {
-                ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
-                          optarg);
-            } else {
-                time_alarm(timeout);
-            }
-            break;
-
-        case 'h':
-            usage();
-
-        case 'V':
-            printf("%s %s compiled "__DATE__" "__TIME__"\n",
-                   program_name, VERSION BUILDNR);
-            exit(EXIT_SUCCESS);
-
-        case 'v':
-            vlog_set_verbosity(optarg);
-            break;
-
-        case OPT_STRICT:
-            s->strict = true;
-            break;
-
-        VCONN_SSL_OPTION_HANDLERS
-
-        case '?':
-            exit(EXIT_FAILURE);
-
-        default:
-            abort();
-        }
-    }
-    free(short_options);
-}
-
-static void
-usage(void)
-{
-    printf("%s: OpenVSwitch datapath and OpenFlow switch management utility\n"
-           "usage: %s [OPTIONS] COMMAND [ARG...]\n"
-           "\nFor OpenVSwitch datapaths:\n"
-           "  adddp DP [IFACE...]         add new datapath DP (with IFACES)\n"
-           "  deldp DP                    delete local datapath DP\n"
-           "  addif DP IFACE...           add each IFACE as a port on DP\n"
-           "  delif DP IFACE...           delete each IFACE from DP\n"
-           "  dp-show                     show basic info on all datapaths\n"
-           "  dp-show DP...               show basic info on each DP\n"
-           "  dp-dump-flows DP            display flows in DP\n"
-           "  dp-del-flows DP             delete all flows from DP\n"
-           "  dp-dump-groups DP           display port groups in DP\n"
-           "\nFor OpenFlow switches:\n"
-           "  show SWITCH                 show OpenFlow information\n"
-           "  status SWITCH [KEY]         report statistics (about KEY)\n"
-           "  dump-desc SWITCH            print switch description\n"
-           "  dump-tables SWITCH          print table stats\n"
-           "  mod-port SWITCH IFACE ACT   modify port behavior\n"
-           "  dump-ports SWITCH           print port statistics\n"
-           "  dump-flows SWITCH           print all flow entries\n"
-           "  dump-flows SWITCH FLOW      print matching FLOWs\n"
-           "  dump-aggregate SWITCH       print aggregate flow statistics\n"
-           "  dump-aggregate SWITCH FLOW  print aggregate stats for FLOWs\n"
-#ifdef SUPPORT_SNAT
-           "  add-snat SWITCH IFACE IP    add SNAT config to IFACE\n"
-           "  del-snat SWITCH IFACE       delete SNAT config on IFACE\n"
-#endif
-           "  add-flow SWITCH FLOW        add flow described by FLOW\n"
-           "  add-flows SWITCH FILE       add flows from FILE\n"
-           "  mod-flows SWITCH FLOW       modify actions of matching FLOWs\n"
-           "  del-flows SWITCH [FLOW]     delete matching FLOWs\n"
-           "  monitor SWITCH MISSLEN EXP  print packets received from SWITCH\n"
-           "  execute SWITCH CMD [ARG...] execute CMD with ARGS on SWITCH\n"
-           "\nFor OpenFlow switches and controllers:\n"
-           "  probe VCONN                 probe whether VCONN is up\n"
-           "  ping VCONN [N]              latency of N-byte echos\n"
-           "  benchmark VCONN N COUNT     bandwidth of COUNT N-byte echos\n"
-           "where each SWITCH is an active OpenFlow connection method.\n",
-           program_name, program_name);
-    vconn_usage(true, false, false);
-    vlog_usage();
-    printf("\nOther options:\n"
-           "  --strict                    use strict match for flow commands\n"
-           "  -t, --timeout=SECS          give up after SECS seconds\n"
-           "  -h, --help                  display this help message\n"
-           "  -V, --version               display version information\n");
-    exit(EXIT_SUCCESS);
-}
-
-static void run(int retval, const char *message, ...)
-    PRINTF_FORMAT(2, 3);
-
-static void run(int retval, const char *message, ...)
-{
-    if (retval) {
-        va_list args;
-
-        fprintf(stderr, "%s: ", program_name);
-        va_start(args, message);
-        vfprintf(stderr, message, args);
-        va_end(args);
-        if (retval == EOF) {
-            fputs(": unexpected end of file\n", stderr);
-        } else {
-            fprintf(stderr, ": %s\n", strerror(retval));
-        }
-
-        exit(EXIT_FAILURE);
-    }
-}
-\f
-static void do_add_port(const struct settings *, int argc, char *argv[]);
-
-static int if_up(const char *netdev_name)
-{
-    struct netdev *netdev;
-    int retval;
-
-    retval = netdev_open(netdev_name, NETDEV_ETH_TYPE_NONE, &netdev);
-    if (!retval) {
-        retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
-        netdev_close(netdev);
-    }
-    return retval;
-}
-
-static void
-do_get_idx(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct dpif dpif;
-    run(dpif_open(argv[1], &dpif), "opening datapath");
-    printf("%u\n", dpif_id(&dpif));
-    dpif_close(&dpif);
-}
-
-static void
-do_get_name(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct dpif dpif;
-    char name[IF_NAMESIZE + 1];
-
-    run(dpif_open(argv[1], &dpif), "opening datapath");
-    run(dpif_get_name(&dpif, name, sizeof name), "getting datapath name");
-    puts(name);
-    dpif_close(&dpif);
-}
-
-static void
-do_add_dp(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct dpif dpif;
-    run(dpif_create(argv[1], &dpif), "add_dp");
-    dpif_close(&dpif);
-    if (argc > 2) {
-        do_add_port(s, argc, argv);
-    }
-}
-
-static void
-do_del_dp(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct dpif dpif;
-    run(dpif_open(argv[1], &dpif), "opening datapath");
-    run(dpif_delete(&dpif), "del_dp");
-    dpif_close(&dpif);
-}
-
-static int
-compare_ports(const void *a_, const void *b_)
-{
-    const struct odp_port *a = a_;
-    const struct odp_port *b = b_;
-    return a->port < b->port ? -1 : a->port > b->port;
-}
-
-static void
-query_ports(struct dpif *dpif, struct odp_port **ports, size_t *n_ports)
-{
-    run(dpif_port_list(dpif, ports, n_ports), "listing ports");
-    qsort(*ports, *n_ports, sizeof **ports, compare_ports);
-}
-
-static uint16_t
-get_free_port(struct dpif *dpif)
-{
-    struct odp_port *ports;
-    size_t n_ports;
-    int port_no;
-
-    query_ports(dpif, &ports, &n_ports);
-    for (port_no = 0; port_no <= UINT16_MAX; port_no++) {
-        size_t i;
-        for (i = 0; i < n_ports; i++) {
-            if (ports[i].port == port_no) {
-                goto next_portno;
-            }
-        }
-        free(ports);
-        return port_no;
-
-    next_portno: ;
-    }
-    ovs_fatal(0, "no free datapath ports");
-}
-
-static void
-do_add_port(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    bool failure = false;
-    struct dpif dpif;
-    int i;
-
-    run(dpif_open(argv[1], &dpif), "opening datapath");
-    for (i = 2; i < argc; i++) {
-        char *save_ptr = NULL;
-        char *devname, *suboptions;
-        int port = -1;
-        int flags = 0;
-        int error;
-
-        devname = strtok_r(argv[i], ",,", &save_ptr);
-        if (!devname) {
-            ovs_error(0, "%s is not a valid network device name", argv[i]);
-            continue;
-        }
-
-        suboptions = strtok_r(NULL, "", &save_ptr);
-        if (suboptions) {
-            enum {
-                AP_PORT,
-                AP_INTERNAL
-            };
-            static char *options[] = {
-                "port",
-                "internal"
-            };
-
-            while (*suboptions != '\0') {
-                char *value;
-
-                switch (getsubopt(&suboptions, options, &value)) {
-                case AP_PORT:
-                    if (!value) {
-                        ovs_error(0, "'port' suboption requires a value");
-                    }
-                    port = atoi(value);
-                    break;
-
-                case AP_INTERNAL:
-                    flags |= ODP_PORT_INTERNAL;
-                    break;
-
-                default:
-                    ovs_error(0, "unknown suboption '%s'", value);
-                    break;
-                }
-            }
-        }
-        if (port < 0) {
-            port = get_free_port(&dpif);
-        }
-
-        error = dpif_port_add(&dpif, devname, port, flags);
-        if (error) {
-            ovs_error(error, "adding %s as port %"PRIu16" of %s failed",
-                      devname, port, argv[1]);
-            failure = true;
-        } else if (if_up(devname)) {
-            failure = true;
-        }
-    }
-    dpif_close(&dpif);
-    if (failure) {
-        exit(EXIT_FAILURE);
-    }
-}
-
-static bool
-get_port_number(struct dpif *dpif, const char *name, uint16_t *port)
-{
-    struct odp_port *ports;
-    size_t n_ports;
-    size_t i;
-
-    query_ports(dpif, &ports, &n_ports);
-    for (i = 0; i < n_ports; i++) {
-        if (!strcmp(name, ports[i].devname)) {
-            *port = ports[i].port;
-            free(ports);
-            return true;
-        }
-    }
-    free(ports);
-    ovs_error(0, "no port named %s", name);
-    return false;
-}
-
-static void
-do_del_port(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    bool failure = false;
-    struct dpif dpif;
-    int i;
-
-    run(dpif_open(argv[1], &dpif), "opening datapath");
-    for (i = 2; i < argc; i++) {
-        const char *name = argv[i];
-        uint16_t port;
-        int error;
-
-        if (!name[strspn(name, "0123456789")]) {
-            port = atoi(name);
-        } else if (!get_port_number(&dpif, name, &port)) {
-            failure = true;
-            continue;
-        }
-
-        error = dpif_port_del(&dpif, port);
-        if (error) {
-            ovs_error(error, "deleting port %s from %s failed", name, argv[1]);
-            failure = true;
-        }
-    }
-    dpif_close(&dpif);
-    if (failure) {
-        exit(EXIT_FAILURE);
-    }
-}
-
-static void
-show_dpif(struct dpif *dpif)
-{
-    struct odp_port *ports;
-    struct odp_stats stats;
-    size_t n_ports;
-    size_t i;
-
-    printf("dp%u:\n", dpif_id(dpif));
-    if (!dpif_get_dp_stats(dpif, &stats)) {
-        printf("\tflows: cur:%"PRIu32", soft-max:%"PRIu32", "
-               "hard-max:%"PRIu32"\n",
-               stats.n_flows, stats.cur_capacity, stats.max_capacity);
-        printf("\tports: cur:%"PRIu32", max:%"PRIu32"\n",
-               stats.n_ports, stats.max_ports);
-        printf("\tgroups: max:%"PRIu16"\n", stats.max_groups);
-        printf("\tlookups: frags:%"PRIu64", hit:%"PRIu64", missed:%"PRIu64", "
-               "lost:%"PRIu64"\n",
-               stats.n_frags, stats.n_hit, stats.n_missed, stats.n_lost);
-        printf("\tqueues: max-miss:%"PRIu16", max-action:%"PRIu16"\n",
-               stats.max_miss_queue, stats.max_action_queue);
-    }
-    query_ports(dpif, &ports, &n_ports);
-    for (i = 0; i < n_ports; i++) {
-        printf("\tport %u: %s", ports[i].port, ports[i].devname);
-        if (ports[i].flags & ODP_PORT_INTERNAL) {
-            printf(" (internal)");
-        }
-        printf("\n");
-    }
-    free(ports);
-    dpif_close(dpif);
-}
-
-static void
-do_dp_show(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    bool failure = false;
-    if (argc > 1) {
-        int i;
-        for (i = 1; i < argc; i++) {
-            const char *name = argv[i];
-            struct dpif dpif;
-            int error;
-
-            error = dpif_open(name, &dpif);
-            if (!error) {
-                show_dpif(&dpif);
-            } else {
-                ovs_error(error, "opening datapath %s failed", name);
-                failure = true;
-            }
-        }
-    } else {
-        unsigned int i;
-        for (i = 0; i < ODP_MAX; i++) {
-            char name[128];
-            struct dpif dpif;
-            int error;
-
-            sprintf(name, "dp%u", i);
-            error = dpif_open(name, &dpif);
-            if (!error) {
-                show_dpif(&dpif);
-            } else if (error != ENODEV) {
-                ovs_error(error, "opening datapath %s failed", name);
-                failure = true;
-            }
-        }
-    }
-    if (failure) {
-        exit(EXIT_FAILURE);
-    }
-}
-
-static void
-do_dp_dump_flows(const struct settings *s UNUSED,
-                 int argc UNUSED, char *argv[])
-{
-    struct odp_flow *flows;
-    struct dpif dpif;
-    size_t n_flows;
-    struct ds ds;
-    size_t i;
-
-    run(dpif_open(argv[1], &dpif), "opening datapath");
-    run(dpif_flow_list_all(&dpif, &flows, &n_flows), "listing all flows");
-
-    ds_init(&ds);
-    for (i = 0; i < n_flows; i++) {
-        struct odp_flow *f = &flows[i];
-        enum { MAX_ACTIONS = 4096 / sizeof(union odp_action) };
-        union odp_action actions[MAX_ACTIONS];
-
-        f->actions = actions;
-        f->n_actions = MAX_ACTIONS;
-        dpif_flow_get(&dpif, f);
-
-        ds_clear(&ds);
-        format_odp_flow(&ds, f);
-        printf("%s\n", ds_cstr(&ds));
-    }
-    ds_destroy(&ds);
-    dpif_close(&dpif);
-}
-
-static void
-do_dp_del_flows(const struct settings *s UNUSED,
-                 int argc UNUSED, char *argv[])
-{
-    struct dpif dpif;
-
-    run(dpif_open(argv[1], &dpif), "opening datapath");
-    run(dpif_flow_flush(&dpif), "deleting all flows");
-    dpif_close(&dpif);
-}
-
-static void
-do_dp_dump_groups(const struct settings *s UNUSED,
-                  int argc UNUSED, char *argv[])
-{
-    struct odp_stats stats;
-    struct dpif dpif;
-    unsigned int i;
-
-    run(dpif_open(argv[1], &dpif), "opening datapath");
-    run(dpif_get_dp_stats(&dpif, &stats), "get datapath stats");
-    for (i = 0; i < stats.max_groups; i++) {
-        uint16_t ports[UINT16_MAX];
-        size_t n_ports;
-
-        if (!dpif_port_group_get(&dpif, i, ports,
-                                 ARRAY_SIZE(ports), &n_ports) && n_ports) {
-            size_t j;
-
-            printf("group %u:", i);
-            for (j = 0; j < n_ports; j++) {
-                printf(" %"PRIu16, ports[j]);
-            }
-            printf("\n");
-        }
-    }
-    dpif_close(&dpif);
-}
-\f
-/* Generic commands. */
-
-static void
-open_vconn(const char *name, struct vconn **vconnp)
-{
-    struct dpif dpif;
-    struct stat s;
-
-    if (strstr(name, ":")) {
-        run(vconn_open_block(name, OFP_VERSION, vconnp),
-            "connecting to %s", name);
-    } else if (!stat(name, &s) && S_ISSOCK(s.st_mode)) {
-        char *vconn_name = xasprintf("unix:%s", name);
-        VLOG_INFO("connecting to %s", vconn_name);
-        run(vconn_open_block(vconn_name, OFP_VERSION, vconnp),
-            "connecting to %s", vconn_name);
-        free(vconn_name);
-    } else if (!dpif_open(name, &dpif)) {
-        char dpif_name[IF_NAMESIZE + 1];
-        char *socket_name;
-        char *vconn_name;
-
-        run(dpif_get_name(&dpif, dpif_name, sizeof dpif_name),
-            "obtaining name of %s", dpif_name);
-        dpif_close(&dpif);
-        if (strcmp(dpif_name, name)) {
-            VLOG_INFO("datapath %s is named %s", name, dpif_name);
-        }
-
-        socket_name = xasprintf("%s/%s.mgmt", ovs_rundir, dpif_name);
-        if (stat(socket_name, &s)) {
-            ovs_fatal(errno, "cannot connect to %s: stat failed on %s",
-                      name, socket_name);
-        } else if (!S_ISSOCK(s.st_mode)) {
-            ovs_fatal(0, "cannot connect to %s: %s is not a socket",
-                      name, socket_name);
-        }
-
-        vconn_name = xasprintf("unix:%s", socket_name);
-        VLOG_INFO("connecting to %s", vconn_name);
-        run(vconn_open_block(vconn_name, OFP_VERSION, vconnp),
-            "connecting to %s", vconn_name);
-        free(socket_name);
-        free(vconn_name);
-    } else {
-        ovs_fatal(0, "%s is not a valid connection method", name);
-    }
-}
-
-static void *
-alloc_stats_request(size_t body_len, uint16_t type, struct ofpbuf **bufferp)
-{
-    struct ofp_stats_request *rq;
-    rq = make_openflow((offsetof(struct ofp_stats_request, body)
-                        + body_len), OFPT_STATS_REQUEST, bufferp);
-    rq->type = htons(type);
-    rq->flags = htons(0);
-    return rq->body;
-}
-
-static void
-send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer)
-{
-    update_openflow_length(buffer);
-    run(vconn_send_block(vconn, buffer), "failed to send packet to switch");
-}
-
-static void
-dump_transaction(const char *vconn_name, struct ofpbuf *request)
-{
-    struct vconn *vconn;
-    struct ofpbuf *reply;
-
-    update_openflow_length(request);
-    open_vconn(vconn_name, &vconn);
-    run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
-    ofp_print(stdout, reply->data, reply->size, 1);
-    vconn_close(vconn);
-}
-
-static void
-dump_trivial_transaction(const char *vconn_name, uint8_t request_type)
-{
-    struct ofpbuf *request;
-    make_openflow(sizeof(struct ofp_header), request_type, &request);
-    dump_transaction(vconn_name, request);
-}
-
-static void
-dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
-{
-    uint32_t send_xid = ((struct ofp_header *) request->data)->xid;
-    struct vconn *vconn;
-    bool done = false;
-
-    open_vconn(vconn_name, &vconn);
-    send_openflow_buffer(vconn, request);
-    while (!done) {
-        uint32_t 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) {
-            struct ofp_stats_reply *osr;
-
-            ofp_print(stdout, reply->data, reply->size, 1);
-
-            osr = ofpbuf_at(reply, 0, sizeof *osr);
-            done = !osr || !(ntohs(osr->flags) & OFPSF_REPLY_MORE);
-        } else {
-            VLOG_DBG("received reply with xid %08"PRIx32" "
-                     "!= expected %08"PRIx32, recv_xid, send_xid);
-        }
-        ofpbuf_delete(reply);
-    }
-    vconn_close(vconn);
-}
-
-static void
-dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
-{
-    struct ofpbuf *request;
-    alloc_stats_request(0, stats_type, &request);
-    dump_stats_transaction(vconn_name, request);
-}
-
-static void
-do_show(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
-    dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
-}
-
-static void
-do_status(const struct settings *s UNUSED, int argc, char *argv[])
-{
-    struct nicira_header *request, *reply;
-    struct vconn *vconn;
-    struct ofpbuf *b;
-
-    request = make_openflow(sizeof *request, OFPT_VENDOR, &b);
-    request->vendor = htonl(NX_VENDOR_ID);
-    request->subtype = htonl(NXT_STATUS_REQUEST);
-    if (argc > 2) {
-        ofpbuf_put(b, argv[2], strlen(argv[2]));
-        update_openflow_length(b);
-    }
-    open_vconn(argv[1], &vconn);
-    run(vconn_transact(vconn, b, &b), "talking to %s", argv[1]);
-    vconn_close(vconn);
-
-    if (b->size < sizeof *reply) {
-        ovs_fatal(0, "short reply (%zu bytes)", b->size);
-    }
-    reply = b->data;
-    if (reply->header.type != OFPT_VENDOR
-        || reply->vendor != ntohl(NX_VENDOR_ID)
-        || reply->subtype != ntohl(NXT_STATUS_REPLY)) {
-        ofp_print(stderr, b->data, b->size, 2);
-        ovs_fatal(0, "bad reply");
-    }
-
-    fwrite(reply + 1, b->size - sizeof *reply, 1, stdout);
-}
-
-static void
-do_dump_desc(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    dump_trivial_stats_transaction(argv[1], OFPST_DESC);
-}
-
-static void
-do_dump_tables(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
-}
-
-
-static uint32_t
-str_to_u32(const char *str) 
-{
-    char *tail;
-    uint32_t value;
-
-    errno = 0;
-    value = strtoul(str, &tail, 0);
-    if (errno == EINVAL || errno == ERANGE || *tail) {
-        ovs_fatal(0, "invalid numeric format %s", str);
-    }
-    return value;
-}
-
-static void
-str_to_mac(const char *str, uint8_t mac[6]) 
-{
-    if (sscanf(str, "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8,
-               &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) {
-        ovs_fatal(0, "invalid mac address %s", str);
-    }
-}
-
-static uint32_t
-str_to_ip(const char *str_, uint32_t *ip)
-{
-    char *str = xstrdup(str_);
-    char *save_ptr = NULL;
-    const char *name, *netmask;
-    struct in_addr in_addr;
-    int n_wild, retval;
-
-    name = strtok_r(str, "//", &save_ptr);
-    retval = name ? lookup_ip(name, &in_addr) : EINVAL;
-    if (retval) {
-        ovs_fatal(0, "%s: could not convert to IP address", str);
-    }
-    *ip = in_addr.s_addr;
-
-    netmask = strtok_r(NULL, "//", &save_ptr);
-    if (netmask) {
-        uint8_t o[4];
-        if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8,
-                   &o[0], &o[1], &o[2], &o[3]) == 4) {
-            uint32_t nm = (o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3];
-            int i;
-
-            /* Find first 1-bit. */
-            for (i = 0; i < 32; i++) {
-                if (nm & (1u << i)) {
-                    break;
-                }
-            }
-            n_wild = i;
-
-            /* Verify that the rest of the bits are 1-bits. */
-            for (; i < 32; i++) {
-                if (!(nm & (1u << i))) {
-                    ovs_fatal(0, "%s: %s is not a valid netmask",
-                              str, netmask);
-                }
-            }
-        } else {
-            int prefix = atoi(netmask);
-            if (prefix <= 0 || prefix > 32) {
-                ovs_fatal(0, "%s: network prefix bits not between 1 and 32",
-                          str);
-            }
-            n_wild = 32 - prefix;
-        }
-    } else {
-        n_wild = 0;
-    }
-
-    free(str);
-    return n_wild;
-}
-
-static void *
-put_action(struct ofpbuf *b, size_t size, uint16_t type)
-{
-    struct ofp_action_header *ah = ofpbuf_put_zeros(b, size);
-    ah->type = htons(type);
-    ah->len = htons(size);
-    return ah;
-}
-
-static struct ofp_action_output *
-put_output_action(struct ofpbuf *b, uint16_t port)
-{
-    struct ofp_action_output *oao = put_action(b, sizeof *oao, OFPAT_OUTPUT);
-    oao->port = htons(port);
-    return oao;
-}
-
-static void
-put_dl_addr_action(struct ofpbuf *b, uint16_t type, const char *addr)
-{
-    struct ofp_action_dl_addr *oada = put_action(b, sizeof *oada, type);
-    str_to_mac(addr, oada->dl_addr);
-}
-
-
-static bool
-parse_port_name(const char *name, uint16_t *port)
-{
-    struct pair {
-        const char *name;
-        uint16_t value;
-    };
-    static const struct pair pairs[] = {
-#define DEF_PAIR(NAME) {#NAME, OFPP_##NAME}
-        DEF_PAIR(IN_PORT),
-        DEF_PAIR(TABLE),
-        DEF_PAIR(NORMAL),
-        DEF_PAIR(FLOOD),
-        DEF_PAIR(ALL),
-        DEF_PAIR(CONTROLLER),
-        DEF_PAIR(LOCAL),
-        DEF_PAIR(NONE),
-#undef DEF_PAIR
-    };
-    static const int n_pairs = ARRAY_SIZE(pairs);
-    size_t i;
-
-    for (i = 0; i < n_pairs; i++) {
-        if (!strcasecmp(name, pairs[i].name)) {
-            *port = pairs[i].value;
-            return true;
-        }
-    }
-    return false;
-}
-
-static void
-str_to_action(char *str, struct ofpbuf *b)
-{
-    char *act, *arg;
-    char *saveptr = NULL;
-    bool drop = false;
-    int n_actions;
-
-    for (act = strtok_r(str, ", \t\r\n", &saveptr), n_actions = 0; act;
-         act = strtok_r(NULL, ", \t\r\n", &saveptr), n_actions++) 
-    {
-        uint16_t port;
-
-        if (drop) {
-            ovs_fatal(0, "Drop actions must not be followed by other actions");
-        }
-
-        /* Arguments are separated by colons */
-        arg = strchr(act, ':');
-        if (arg) {
-            *arg = '\0';
-            arg++;
-        }
-
-        if (!strcasecmp(act, "mod_vlan_vid")) {
-            struct ofp_action_vlan_vid *va;
-            va = put_action(b, sizeof *va, OFPAT_SET_VLAN_VID);
-            va->vlan_vid = htons(str_to_u32(arg));
-        } else if (!strcasecmp(act, "mod_vlan_pcp")) {
-            struct ofp_action_vlan_pcp *va;
-            va = put_action(b, sizeof *va, OFPAT_SET_VLAN_PCP);
-            va->vlan_pcp = str_to_u32(arg);
-        } else if (!strcasecmp(act, "strip_vlan")) {
-            struct ofp_action_header *ah;
-            ah = put_action(b, sizeof *ah, OFPAT_STRIP_VLAN);
-            ah->type = htons(OFPAT_STRIP_VLAN);
-        } else if (!strcasecmp(act, "mod_dl_src")) {
-            put_dl_addr_action(b, OFPAT_SET_DL_SRC, arg);
-        } else if (!strcasecmp(act, "mod_dl_dst")) {
-            put_dl_addr_action(b, OFPAT_SET_DL_DST, arg);
-        } else if (!strcasecmp(act, "output")) {
-            put_output_action(b, str_to_u32(arg));
-        } else if (!strcasecmp(act, "drop")) {
-            /* A drop action in OpenFlow occurs by just not setting 
-             * an action. */
-            drop = true;
-            if (n_actions) {
-                ovs_fatal(0, "Drop actions must not be preceded by other "
-                          "actions");
-            }
-#ifdef SUPPORT_SNAT
-        } else if (!strcasecmp(act, "nat")) {
-            struct nx_action_snat *sa;
-
-            if (str_to_u32(arg) > OFPP_MAX) {
-                ovs_fatal(0, "Invalid nat port: %s\n", arg);
-            }
-
-            sa = put_action(b, sizeof *sa, OFPAT_VENDOR);
-            sa->vendor = htonl(NX_VENDOR_ID);
-            sa->subtype = htons(NXAST_SNAT);
-            sa->port = htons(str_to_u32(arg));
-#endif
-        } else if (!strcasecmp(act, "CONTROLLER")) {
-            struct ofp_action_output *oao;
-            oao = put_output_action(b, OFPP_CONTROLLER);
-
-            /* Unless a numeric argument is specified, we send the whole
-             * packet to the controller. */
-            if (arg && (strspn(act, "0123456789") == strlen(act))) {
-               oao->max_len = htons(str_to_u32(arg));
-            }
-        } else if (parse_port_name(act, &port)) {
-            put_output_action(b, port);
-        } else if (strspn(act, "0123456789") == strlen(act)) {
-            put_output_action(b, str_to_u32(act));
-        } else {
-            ovs_fatal(0, "Unknown action: %s", act);
-        }
-    }
-}
-
-struct protocol {
-    const char *name;
-    uint16_t dl_type;
-    uint8_t nw_proto;
-};
-
-static bool
-parse_protocol(const char *name, const struct protocol **p_out)
-{
-    static const struct protocol protocols[] = {
-        { "ip", ETH_TYPE_IP, 0 },
-        { "arp", ETH_TYPE_ARP, 0 },
-        { "icmp", ETH_TYPE_IP, IP_TYPE_ICMP },
-        { "tcp", ETH_TYPE_IP, IP_TYPE_TCP },
-        { "udp", ETH_TYPE_IP, IP_TYPE_UDP },
-    };
-    const struct protocol *p;
-
-    for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
-        if (!strcmp(p->name, name)) {
-            *p_out = p;
-            return true;
-        }
-    }
-    *p_out = NULL;
-    return false;
-}
-
-struct field {
-    const char *name;
-    uint32_t wildcard;
-    enum { F_U8, F_U16, F_MAC, F_IP } type;
-    size_t offset, shift;
-};
-
-static bool
-parse_field(const char *name, const struct field **f_out) 
-{
-#define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
-    static const struct field fields[] = { 
-        { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port), 0 },
-        { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan), 0 },
-        { "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src), 0 },
-        { "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst), 0 },
-        { "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type), 0 },
-        { "nw_src", OFPFW_NW_SRC_MASK, F_IP,
-          F_OFS(nw_src), OFPFW_NW_SRC_SHIFT },
-        { "nw_dst", OFPFW_NW_DST_MASK, F_IP,
-          F_OFS(nw_dst), OFPFW_NW_DST_SHIFT },
-        { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto), 0 },
-        { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src), 0 },
-        { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst), 0 },
-        { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type), 0 },
-        { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code), 0 }
-    };
-    const struct field *f;
-
-    for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
-        if (!strcmp(f->name, name)) {
-            *f_out = f;
-            return true;
-        }
-    }
-    *f_out = NULL;
-    return false;
-}
-
-static void
-str_to_flow(char *string, struct ofp_match *match, struct ofpbuf *actions,
-            uint8_t *table_idx, uint16_t *out_port, uint16_t *priority, 
-            uint16_t *idle_timeout, uint16_t *hard_timeout)
-{
-    char *save_ptr = NULL;
-    char *name;
-    uint32_t wildcards;
-
-    if (table_idx) {
-        *table_idx = 0xff;
-    }
-    if (out_port) {
-        *out_port = OFPP_NONE;
-    }
-    if (priority) {
-        *priority = OFP_DEFAULT_PRIORITY;
-    }
-    if (idle_timeout) {
-        *idle_timeout = DEFAULT_IDLE_TIMEOUT;
-    }
-    if (hard_timeout) {
-        *hard_timeout = OFP_FLOW_PERMANENT;
-    }
-    if (actions) {
-        char *act_str = strstr(string, "action");
-        if (!act_str) {
-            ovs_fatal(0, "must specify an action");
-        }
-        *(act_str-1) = '\0';
-
-        act_str = strchr(act_str, '=');
-        if (!act_str) {
-            ovs_fatal(0, "must specify an action");
-        }
-
-        act_str++;
-
-        str_to_action(act_str, actions);
-    }
-    memset(match, 0, sizeof *match);
-    wildcards = OFPFW_ALL;
-    for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
-         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
-        const struct protocol *p;
-
-        if (parse_protocol(name, &p)) {
-            wildcards &= ~OFPFW_DL_TYPE;
-            match->dl_type = htons(p->dl_type);
-            if (p->nw_proto) {
-                wildcards &= ~OFPFW_NW_PROTO;
-                match->nw_proto = p->nw_proto;
-            }
-        } else {
-            const struct field *f;
-            char *value;
-
-            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
-            if (!value) {
-                ovs_fatal(0, "field %s missing value", name);
-            }
-        
-            if (table_idx && !strcmp(name, "table")) {
-                *table_idx = atoi(value);
-            } else if (out_port && !strcmp(name, "out_port")) {
-                *out_port = atoi(value);
-            } else if (priority && !strcmp(name, "priority")) {
-                *priority = atoi(value);
-            } else if (idle_timeout && !strcmp(name, "idle_timeout")) {
-                *idle_timeout = atoi(value);
-            } else if (hard_timeout && !strcmp(name, "hard_timeout")) {
-                *hard_timeout = atoi(value);
-            } else if (parse_field(name, &f)) {
-                void *data = (char *) match + f->offset;
-                if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
-                    wildcards |= f->wildcard;
-                } else {
-                    wildcards &= ~f->wildcard;
-                    if (f->wildcard == OFPFW_IN_PORT
-                        && parse_port_name(value, (uint16_t *) data)) {
-                        /* Nothing to do. */
-                    } else if (f->type == F_U8) {
-                        *(uint8_t *) data = str_to_u32(value);
-                    } else if (f->type == F_U16) {
-                        *(uint16_t *) data = htons(str_to_u32(value));
-                    } else if (f->type == F_MAC) {
-                        str_to_mac(value, data);
-                    } else if (f->type == F_IP) {
-                        wildcards |= str_to_ip(value, data) << f->shift;
-                    } else {
-                        NOT_REACHED();
-                    }
-                }
-            } else {
-                ovs_fatal(0, "unknown keyword %s", name);
-            }
-        }
-    }
-    match->wildcards = htonl(wildcards);
-}
-
-static void
-do_dump_flows(const struct settings *s UNUSED, int argc, char *argv[])
-{
-    struct ofp_flow_stats_request *req;
-    uint16_t out_port;
-    struct ofpbuf *request;
-
-    req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
-    str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
-                &req->table_id, &out_port, NULL, NULL, NULL);
-    memset(&req->pad, 0, sizeof req->pad);
-    req->out_port = htons(out_port);
-
-    dump_stats_transaction(argv[1], request);
-}
-
-static void
-do_dump_aggregate(const struct settings *s UNUSED, int argc, char *argv[])
-{
-    struct ofp_aggregate_stats_request *req;
-    struct ofpbuf *request;
-    uint16_t out_port;
-
-    req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
-    str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
-                &req->table_id, &out_port, NULL, NULL, NULL);
-    memset(&req->pad, 0, sizeof req->pad);
-    req->out_port = htons(out_port);
-
-    dump_stats_transaction(argv[1], request);
-}
-
-#ifdef SUPPORT_SNAT
-static void
-do_add_snat(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct vconn *vconn;
-    struct ofpbuf *buffer;
-    struct nx_act_config *nac;
-    size_t size;
-
-    /* Parse and send. */
-    size = sizeof *nac + sizeof nac->snat[0];
-    nac = make_openflow(size, OFPT_VENDOR, &buffer);
-
-    nac->header.vendor = htonl(NX_VENDOR_ID);
-    nac->header.subtype = htonl(NXT_ACT_SET_CONFIG);
-
-    nac->type = htons(NXAST_SNAT);
-    nac->snat[0].command = NXSC_ADD;
-    nac->snat[0].port = htons(str_to_u32(argv[2]));
-    nac->snat[0].mac_timeout = htons(0);
-    str_to_ip(argv[3], &nac->snat[0].ip_addr_start);
-    str_to_ip(argv[3], &nac->snat[0].ip_addr_end);
-
-    open_vconn(argv[1], &vconn);
-    send_openflow_buffer(vconn, buffer);
-    vconn_close(vconn);
-}
-
-static void
-do_del_snat(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct vconn *vconn;
-    struct ofpbuf *buffer;
-    struct nx_act_config *nac;
-    size_t size;
-
-    /* Parse and send. */
-    size = sizeof *nac + sizeof nac->snat[0];
-    nac = make_openflow(size, OFPT_VENDOR, &buffer);
-
-    nac->header.vendor = htonl(NX_VENDOR_ID);
-    nac->header.subtype = htonl(NXT_ACT_SET_CONFIG);
-
-    nac->type = htons(NXAST_SNAT);
-    nac->snat[0].command = NXSC_DELETE;
-    nac->snat[0].port = htons(str_to_u32(argv[2]));
-    nac->snat[0].mac_timeout = htons(0);
-
-    open_vconn(argv[1], &vconn);
-    send_openflow_buffer(vconn, buffer);
-    vconn_close(vconn);
-}
-#endif /* SUPPORT_SNAT */
-
-static void
-do_add_flow(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct vconn *vconn;
-    struct ofpbuf *buffer;
-    struct ofp_flow_mod *ofm;
-    uint16_t priority, idle_timeout, hard_timeout;
-    struct ofp_match match;
-
-    /* Parse and send.  str_to_flow() will expand and reallocate the data in
-     * 'buffer', so we can't keep pointers to across the str_to_flow() call. */
-    make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
-    str_to_flow(argv[2], &match, buffer,
-                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
-    ofm = buffer->data;
-    ofm->match = match;
-    ofm->command = htons(OFPFC_ADD);
-    ofm->idle_timeout = htons(idle_timeout);
-    ofm->hard_timeout = htons(hard_timeout);
-    ofm->buffer_id = htonl(UINT32_MAX);
-    ofm->priority = htons(priority);
-    ofm->reserved = htonl(0);
-
-    open_vconn(argv[1], &vconn);
-    send_openflow_buffer(vconn, buffer);
-    vconn_close(vconn);
-}
-
-static void
-do_add_flows(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct vconn *vconn;
-    FILE *file;
-    char line[1024];
-
-    file = fopen(argv[2], "r");
-    if (file == NULL) {
-        ovs_fatal(errno, "%s: open", argv[2]);
-    }
-
-    open_vconn(argv[1], &vconn);
-    while (fgets(line, sizeof line, file)) {
-        struct ofpbuf *buffer;
-        struct ofp_flow_mod *ofm;
-        uint16_t priority, idle_timeout, hard_timeout;
-        struct ofp_match match;
-
-        char *comment;
-
-        /* Delete comments. */
-        comment = strchr(line, '#');
-        if (comment) {
-            *comment = '\0';
-        }
-
-        /* Drop empty lines. */
-        if (line[strspn(line, " \t\n")] == '\0') {
-            continue;
-        }
-
-        /* Parse and send.  str_to_flow() will expand and reallocate the data
-         * in 'buffer', so we can't keep pointers to across the str_to_flow()
-         * call. */
-        ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
-        str_to_flow(line, &match, buffer,
-                    NULL, NULL, &priority, &idle_timeout, &hard_timeout);
-        ofm = buffer->data;
-        ofm->match = match;
-        ofm->command = htons(OFPFC_ADD);
-        ofm->idle_timeout = htons(idle_timeout);
-        ofm->hard_timeout = htons(hard_timeout);
-        ofm->buffer_id = htonl(UINT32_MAX);
-        ofm->priority = htons(priority);
-        ofm->reserved = htonl(0);
-
-        send_openflow_buffer(vconn, buffer);
-    }
-    vconn_close(vconn);
-    fclose(file);
-}
-
-static void
-do_mod_flows(const struct settings *s, int argc UNUSED, char *argv[])
-{
-    uint16_t priority, idle_timeout, hard_timeout;
-    struct vconn *vconn;
-    struct ofpbuf *buffer;
-    struct ofp_flow_mod *ofm;
-
-    /* Parse and send. */
-    ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
-    str_to_flow(argv[2], &ofm->match, buffer,
-                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
-    if (s->strict) {
-        ofm->command = htons(OFPFC_MODIFY_STRICT);
-    } else {
-        ofm->command = htons(OFPFC_MODIFY);
-    }
-    ofm->idle_timeout = htons(idle_timeout);
-    ofm->hard_timeout = htons(hard_timeout);
-    ofm->buffer_id = htonl(UINT32_MAX);
-    ofm->priority = htons(priority);
-    ofm->reserved = htonl(0);
-
-    open_vconn(argv[1], &vconn);
-    send_openflow_buffer(vconn, buffer);
-    vconn_close(vconn);
-}
-
-static void do_del_flows(const struct settings *s, int argc, char *argv[])
-{
-    struct vconn *vconn;
-    uint16_t priority;
-    uint16_t out_port;
-    struct ofpbuf *buffer;
-    struct ofp_flow_mod *ofm;
-
-    /* Parse and send. */
-    ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
-    str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL, 
-                &out_port, &priority, NULL, NULL);
-    if (s->strict) {
-        ofm->command = htons(OFPFC_DELETE_STRICT);
-    } else {
-        ofm->command = htons(OFPFC_DELETE);
-    }
-    ofm->idle_timeout = htons(0);
-    ofm->hard_timeout = htons(0);
-    ofm->buffer_id = htonl(UINT32_MAX);
-    ofm->out_port = htons(out_port);
-    ofm->priority = htons(priority);
-    ofm->reserved = htonl(0);
-
-    open_vconn(argv[1], &vconn);
-    send_openflow_buffer(vconn, buffer);
-    vconn_close(vconn);
-}
-
-static void
-do_monitor(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct vconn *vconn;
-
-    open_vconn(argv[1], &vconn);
-    if (argc > 2) {
-        int miss_send_len = atoi(argv[2]);
-        int send_flow_exp = argc > 3 ? atoi(argv[3]) : 0;
-        struct ofp_switch_config *osc;
-        struct ofpbuf *buf;
-
-        osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf);
-        osc->flags = htons(send_flow_exp ? OFPC_SEND_FLOW_EXP : 0);
-        osc->miss_send_len = htons(miss_send_len);
-        send_openflow_buffer(vconn, buf);
-    }
-    for (;;) {
-        struct ofpbuf *b;
-        run(vconn_recv_block(vconn, &b), "vconn_recv");
-        ofp_print(stderr, b->data, b->size, 2);
-        ofpbuf_delete(b);
-    }
-}
-
-static void
-do_dump_ports(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    dump_trivial_stats_transaction(argv[1], OFPST_PORT);
-}
-
-static void
-do_probe(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct ofpbuf *request;
-    struct vconn *vconn;
-    struct ofpbuf *reply;
-
-    make_openflow(sizeof(struct ofp_header), OFPT_ECHO_REQUEST, &request);
-    open_vconn(argv[1], &vconn);
-    run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
-    if (reply->size != sizeof(struct ofp_header)) {
-        ovs_fatal(0, "reply does not match request");
-    }
-    ofpbuf_delete(reply);
-    vconn_close(vconn);
-}
-
-static void
-do_mod_port(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    struct ofpbuf *request, *reply;
-    struct ofp_switch_features *osf;
-    struct ofp_port_mod *opm;
-    struct vconn *vconn;
-    char *endptr;
-    int n_ports;
-    int port_idx;
-    int port_no;
-    
-
-    /* Check if the argument is a port index.  Otherwise, treat it as
-     * the port name. */
-    port_no = strtol(argv[2], &endptr, 10);
-    if (port_no == 0 && endptr == argv[2]) {
-        port_no = -1;
-    }
-
-    /* Send a "Features Request" to get the information we need in order 
-     * to modify the port. */
-    make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request);
-    open_vconn(argv[1], &vconn);
-    run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
-
-    osf = reply->data;
-    n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports;
-
-    for (port_idx = 0; port_idx < n_ports; port_idx++) {
-        if (port_no != -1) {
-            /* Check argument as a port index */
-            if (osf->ports[port_idx].port_no == htons(port_no)) {
-                break;
-            }
-        } else {
-            /* Check argument as an interface name */
-            if (!strncmp((char *)osf->ports[port_idx].name, argv[2], 
-                        sizeof osf->ports[0].name)) {
-                break;
-            }
-
-        }
-    }
-    if (port_idx == n_ports) {
-        ovs_fatal(0, "couldn't find monitored port: %s", argv[2]);
-    }
-
-    opm = make_openflow(sizeof(struct ofp_port_mod), OFPT_PORT_MOD, &request);
-    opm->port_no = osf->ports[port_idx].port_no;
-    memcpy(opm->hw_addr, osf->ports[port_idx].hw_addr, sizeof opm->hw_addr);
-    opm->config = htonl(0);
-    opm->mask = htonl(0);
-    opm->advertise = htonl(0);
-
-    printf("modifying port: %s\n", osf->ports[port_idx].name);
-
-    if (!strncasecmp(argv[3], MOD_PORT_CMD_UP, sizeof MOD_PORT_CMD_UP)) {
-        opm->mask |= htonl(OFPPC_PORT_DOWN);
-    } else if (!strncasecmp(argv[3], MOD_PORT_CMD_DOWN, 
-                sizeof MOD_PORT_CMD_DOWN)) {
-        opm->mask |= htonl(OFPPC_PORT_DOWN);
-        opm->config |= htonl(OFPPC_PORT_DOWN);
-    } else if (!strncasecmp(argv[3], MOD_PORT_CMD_FLOOD, 
-                sizeof MOD_PORT_CMD_FLOOD)) {
-        opm->mask |= htonl(OFPPC_NO_FLOOD);
-    } else if (!strncasecmp(argv[3], MOD_PORT_CMD_NOFLOOD, 
-                sizeof MOD_PORT_CMD_NOFLOOD)) {
-        opm->mask |= htonl(OFPPC_NO_FLOOD);
-        opm->config |= htonl(OFPPC_NO_FLOOD);
-    } else {
-        ovs_fatal(0, "unknown mod-port command '%s'", argv[3]);
-    }
-
-    send_openflow_buffer(vconn, request);
-
-    ofpbuf_delete(reply);
-    vconn_close(vconn);
-}
-
-static void
-do_ping(const struct settings *s UNUSED, int argc, char *argv[])
-{
-    size_t max_payload = 65535 - sizeof(struct ofp_header);
-    unsigned int payload;
-    struct vconn *vconn;
-    int i;
-
-    payload = argc > 2 ? atoi(argv[2]) : 64;
-    if (payload > max_payload) {
-        ovs_fatal(0, "payload must be between 0 and %zu bytes", max_payload);
-    }
-
-    open_vconn(argv[1], &vconn);
-    for (i = 0; i < 10; i++) {
-        struct timeval start, end;
-        struct ofpbuf *request, *reply;
-        struct ofp_header *rq_hdr, *rpy_hdr;
-
-        rq_hdr = make_openflow(sizeof(struct ofp_header) + payload,
-                               OFPT_ECHO_REQUEST, &request);
-        random_bytes(rq_hdr + 1, payload);
-
-        gettimeofday(&start, NULL);
-        run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact");
-        gettimeofday(&end, NULL);
-
-        rpy_hdr = reply->data;
-        if (reply->size != request->size
-            || memcmp(rpy_hdr + 1, rq_hdr + 1, payload)
-            || rpy_hdr->xid != rq_hdr->xid
-            || rpy_hdr->type != OFPT_ECHO_REPLY) {
-            printf("Reply does not match request.  Request:\n");
-            ofp_print(stdout, request, request->size, 2);
-            printf("Reply:\n");
-            ofp_print(stdout, reply, reply->size, 2);
-        }
-        printf("%d bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
-               reply->size - sizeof *rpy_hdr, argv[1], rpy_hdr->xid,
-                   (1000*(double)(end.tv_sec - start.tv_sec))
-                   + (.001*(end.tv_usec - start.tv_usec)));
-        ofpbuf_delete(request);
-        ofpbuf_delete(reply);
-    }
-    vconn_close(vconn);
-}
-
-static void
-do_benchmark(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
-{
-    size_t max_payload = 65535 - sizeof(struct ofp_header);
-    struct timeval start, end;
-    unsigned int payload_size, message_size;
-    struct vconn *vconn;
-    double duration;
-    int count;
-    int i;
-
-    payload_size = atoi(argv[2]);
-    if (payload_size > max_payload) {
-        ovs_fatal(0, "payload must be between 0 and %zu bytes", max_payload);
-    }
-    message_size = sizeof(struct ofp_header) + payload_size;
-
-    count = atoi(argv[3]);
-
-    printf("Sending %d packets * %u bytes (with header) = %u bytes total\n",
-           count, message_size, count * message_size);
-
-    open_vconn(argv[1], &vconn);
-    gettimeofday(&start, NULL);
-    for (i = 0; i < count; i++) {
-        struct ofpbuf *request, *reply;
-        struct ofp_header *rq_hdr;
-
-        rq_hdr = make_openflow(message_size, OFPT_ECHO_REQUEST, &request);
-        memset(rq_hdr + 1, 0, payload_size);
-        run(vconn_transact(vconn, request, &reply), "transact");
-        ofpbuf_delete(reply);
-    }
-    gettimeofday(&end, NULL);
-    vconn_close(vconn);
-
-    duration = ((1000*(double)(end.tv_sec - start.tv_sec))
-                + (.001*(end.tv_usec - start.tv_usec)));
-    printf("Finished in %.1f ms (%.0f packets/s) (%.0f bytes/s)\n",
-           duration, count / (duration / 1000.0),
-           count * message_size / (duration / 1000.0));
-}
-
-static void
-do_execute(const struct settings *s UNUSED, int argc, char *argv[])
-{
-    struct vconn *vconn;
-    struct ofpbuf *request;
-    struct nicira_header *nicira;
-    struct nx_command_reply *ncr;
-    uint32_t xid;
-    int i;
-
-    nicira = make_openflow(sizeof *nicira, OFPT_VENDOR, &request);
-    xid = nicira->header.xid;
-    nicira->vendor = htonl(NX_VENDOR_ID);
-    nicira->subtype = htonl(NXT_COMMAND_REQUEST);
-    ofpbuf_put(request, argv[2], strlen(argv[2]));
-    for (i = 3; i < argc; i++) {
-        ofpbuf_put_zeros(request, 1);
-        ofpbuf_put(request, argv[i], strlen(argv[i]));
-    }
-    update_openflow_length(request);
-
-    open_vconn(argv[1], &vconn);
-    run(vconn_send_block(vconn, request), "send");
-
-    for (;;) {
-        struct ofpbuf *reply;
-        uint32_t status;
-
-        run(vconn_recv_xid(vconn, xid, &reply), "recv_xid");
-        if (reply->size < sizeof *ncr) {
-            ovs_fatal(0, "reply is too short (%zu bytes < %zu bytes)",
-                      reply->size, sizeof *ncr);
-        }
-        ncr = reply->data;
-        if (ncr->nxh.header.type != OFPT_VENDOR
-            || ncr->nxh.vendor != htonl(NX_VENDOR_ID)
-            || ncr->nxh.subtype != htonl(NXT_COMMAND_REPLY)) {
-            ovs_fatal(0, "reply is invalid");
-        }
-
-        status = ntohl(ncr->status);
-        if (status & NXT_STATUS_STARTED) {
-            /* Wait for a second reply. */
-            continue;
-        } else if (status & NXT_STATUS_EXITED) {
-            fprintf(stderr, "process terminated normally with exit code %d",
-                    status & NXT_STATUS_EXITSTATUS);
-        } else if (status & NXT_STATUS_SIGNALED) {
-            fprintf(stderr, "process terminated by signal %d",
-                    status & NXT_STATUS_TERMSIG);
-        } else if (status & NXT_STATUS_ERROR) {
-            fprintf(stderr, "error executing command");
-        } else {
-            fprintf(stderr, "process terminated for unknown reason");
-        }
-        if (status & NXT_STATUS_COREDUMP) {
-            fprintf(stderr, " (core dumped)");
-        }
-        putc('\n', stderr);
-
-        fwrite(ncr + 1, reply->size - sizeof *ncr, 1, stdout);
-        break;
-    }
-}
-
-static void
-do_help(const struct settings *s UNUSED, int argc UNUSED, char *argv[] UNUSED)
-{
-    usage();
-}
-
-static struct command all_commands[] = {
-#ifdef HAVE_NETLINK
-    { "adddp", 1, INT_MAX, do_add_dp },
-    { "deldp", 1, 1, do_del_dp },
-    { "addif", 2, INT_MAX, do_add_port },
-    { "delif", 2, INT_MAX, do_del_port },
-    { "get-idx", 1, 1, do_get_idx },
-    { "get-name", 1, 1, do_get_name },
-    { "dp-show", 0, INT_MAX, do_dp_show },
-    { "dp-dump-flows", 1, 1, do_dp_dump_flows },
-    { "dp-del-flows", 1, 1, do_dp_del_flows },
-    { "dp-dump-groups", 1, 1, do_dp_dump_groups },
-#endif
-
-    { "show", 1, 1, do_show },
-    { "status", 1, 2, do_status },
-
-    { "help", 0, INT_MAX, do_help },
-    { "monitor", 1, 3, do_monitor },
-    { "dump-desc", 1, 1, do_dump_desc },
-    { "dump-tables", 1, 1, do_dump_tables },
-    { "dump-flows", 1, 2, do_dump_flows },
-    { "dump-aggregate", 1, 2, do_dump_aggregate },
-#ifdef SUPPORT_SNAT
-    { "add-snat", 3, 3, do_add_snat },
-    { "del-snat", 2, 2, do_del_snat },
-#endif
-    { "add-flow", 2, 2, do_add_flow },
-    { "add-flows", 2, 2, do_add_flows },
-    { "mod-flows", 2, 2, do_mod_flows },
-    { "del-flows", 1, 2, do_del_flows },
-    { "dump-ports", 1, 1, do_dump_ports },
-    { "mod-port", 3, 3, do_mod_port },
-    { "probe", 1, 1, do_probe },
-    { "ping", 1, 2, do_ping },
-    { "benchmark", 3, 3, do_benchmark },
-    { "execute", 2, INT_MAX, do_execute },
-    { NULL, 0, 0, NULL },
-};
index 5204a80062625e5a289027061495c9003c42ff56..f4538179dfa546adcc306bc29102ede648d15c6d 100644 (file)
@@ -161,6 +161,6 @@ error occurs.  Use \fB-e help\fR to print a list of available commands.
 
 .SH "SEE ALSO"
 
-.BR dpctl (8),
-.BR secchan (8),
-.BR ovs\-controller (8)
+.BR ovs\-controller (8),
+.BR ovs\-dpctl (8),
+.BR secchan (8)
index 0f8e775faa59be7dd1e7908aa09b6f18b7106dd0..c28efac321662f88562ccedff81d459a8bd0fe64 100644 (file)
@@ -127,6 +127,6 @@ To bind locally to port 6633 (the default) and wait for incoming connections fro
 
 .SH "SEE ALSO"
 
-.BR dpctl (8),
 .BR secchan (8),
-.BR ovs\-appctl (8)
+.BR ovs\-appctl (8),
+.BR ovs\-dpctl (8)
index 12a950e81fb50fc74cf10482b8da1fbdfb4f7982..423fa42c5284687ad227f76890a7030b8f626079 100644 (file)
@@ -100,7 +100,7 @@ effect.
 .SH BUGS
 
 If the network devices specified on the command line have been added
-to an OpenVSwitch datapath with \fBdpctl addif\fR, then controller
+to an OpenVSwitch datapath with \fBovs\-dpctl addif\fR, then controller
 discovery will fail because \fBovs\-discover\fR will not be able to
 see DHCP responses, even though tools such as \fBtcpdump\fR(8) and
 \fBwireshark\fR(1) can see them on the wire.  This is because of the
diff --git a/utilities/ovs-dpctl.8.in b/utilities/ovs-dpctl.8.in
new file mode 100644 (file)
index 0000000..61f2c69
--- /dev/null
@@ -0,0 +1,175 @@
+.TH ovs\-dpctl 8 "March 2009" "OpenVSwitch" "OpenVSwitch Manual"
+.ds PN ovs\-dpctl
+
+.SH NAME
+ovs\-dpctl \- administer OpenVSwitch datapaths
+
+.SH SYNOPSIS
+.B ovs\-dpctl
+[\fIoptions\fR] \fIcommand \fR[\fIswitch\fR] [\fIargs\fR\&...]
+
+.SH DESCRIPTION
+
+The \fBovs\-dpctl\fR program can create, modify, and delete OpenVSwitch
+datapaths.  A single machine may host up to 256 datapaths (numbered 0
+to 255).
+
+A newly created datapath is associated with only one network device, a
+virtual network device sometimes called the datapath's ``local port''.
+A newly created datapath is not, however, associated with any of the
+host's other network devices.  To intercept and process traffic on a
+given network device, use the \fBaddif\fR command to explicitly add
+that network device to the datapath.
+
+Do not use \fBovs\-dpctl\fR commands to modify datapaths if
+\fBvswitchd\fR(8) is in use.  Instead, modify the \fBvswitchd\fR
+configuration file and send \fBSIGHUP\fR to the \fBvswitchd\fR
+process.
+
+.PP
+Most \fBovs\-dpctl\fR commands that work with datapaths take an argument
+that specifies the name of the datapath, in one of the following
+forms:
+
+.so lib/dpif.man
+
+.PP
+The following commands manage datapaths.
+
+.TP
+\fBadddp \fIdp\fR [\fInetdev\fR...]
+
+Creates datapath \fIdp\fR.  The name of the new datapath's local port
+depends on how \fIdp\fR is specified: if it takes the form
+\fBdp\fIN\fR, the local port will be named \fBdp\fIN\fR; if \fIdp\fR
+is \fBnl:\fI, the local port will be named \fBof\fIN\fR; otherwise,
+the local port's name will be \fIdp\fR.
+
+This will fail if the host already has 256 datapaths, if a network
+device with the same name as the new datapath's local port already
+exists, or if \fIdp\fR is given in the form \fBdp\fIN\fR or
+\fBnl:\fIN\fR and a datapath numbered \fIN\fR already exists.
+
+If \fInetdev\fRs are specified, \fBovs\-dpctl\fR adds them to the datapath.
+
+.TP
+\fBdeldp \fIdp\fR
+Deletes datapath \fIdp\fR.  If \fIdp\fR is associated with any network
+devices, they are automatically removed.
+
+.TP
+\fBaddif \fIdp netdev\fR[\fIoption\fR...]...
+Adds each \fInetdev\fR to the set of network devices datapath
+\fIdp\fR monitors, where \fIdp\fR is the name of an existing
+datapath, and \fInetdev\fR is the name of one of the host's
+network devices, e.g. \fBeth0\fR.  Once a network device has been added
+to a datapath, the datapath has complete ownership of the network device's
+traffic and the network device appears silent to the rest of the
+system.
+
+A \fInetdev\fR may be followed by a comma-separated list of options.
+The following options are currently supported:
+
+.RS
+.IP "\fBport=\fIportno\fR"
+Specifies \fIportno\fR (a number between 1 and 255) as the port number
+at which \fInetdev\fR will be attached.  By default, \fBaddif\fR
+automatically selects the lowest available port number.
+
+.IP "\fBinternal\fR"
+Instead of attaching an existing \fInetdev\fR, creates an internal
+port (analogous to the local port) with that name.
+.RE
+
+.TP
+\fBdelif \fIdp netdev\fR...
+Removes each \fInetdev\fR from the list of network devices datapath
+\fIdp\fR monitors.
+
+.TP
+\fBget-idx \fIdp\fR
+Prints the datapath number of datapath \fIdp\fR.
+
+.TP
+\fBget-name \fIdp\fR
+Prints the name of datapath \fIdp\fR, that is, the name of the network
+device acting as its local port.
+
+.TP
+\fBdp-show \fR[\fIdp\fR...]
+Prints a summary of configured datapaths, including their datapath
+numbers and a list of ports connected to each datapath.  (The local
+port is identified as port 0.)
+
+If one or more datapaths are specified, information on only those
+datapaths are displayed.  Otherwise, \fBovs\-dpctl\fR displays information
+about all configured datapaths.
+
+.IP "\fBdp-dump-flows \fIdp\fR"
+Prints to the console all flow entries in datapath \fIdp\fR's
+flow table.
+
+This command is primarily useful for debugging OpenVSwitch.  The flow
+table entries that it displays are not
+OpenFlow flow entries.  Instead, they are different and considerably
+simpler flows maintained by the OpenVSwitch kernel module.
+
+.IP "\fBdp-del-flows \fIdp\fR"
+Deletes all flow entries from datapath \fIdp\fR's flow table.
+
+This command is primarily useful for debugging OpenVSwitch.  As
+discussed in \fBdp-dump-flows\fR, these entries are
+not OpenFlow flow entries.  By deleting them, the process that set them
+up may be confused about their disappearance.
+
+.IP "\fBdp-dump-groups \fIdp\fR"
+Prints to the console the sets of port groups maintained by datapath
+\fIdp\fR.  Ordinarily there are at least 2 port groups in a datapath
+that \fBsecchan\fR or \fBvswitch\fR is controlling: group 0 contains
+all ports except those disabled by STP, and group 1 contains all
+ports.  Additional groups might be used in the future.
+
+This command is primarily useful for debugging OpenVSwitch.  OpenFlow
+does not have a concept of port groups.
+
+.SH OPTIONS
+.TP
+\fB-t\fR, \fB--timeout=\fIsecs\fR
+Limits \fBovs\-dpctl\fR runtime to approximately \fIsecs\fR seconds.  If
+the timeout expires, \fBovs\-dpctl\fR will exit with a \fBSIGALRM\fR
+signal.
+
+.so lib/vlog.man
+.so lib/common.man
+
+.SH EXAMPLES
+
+A typical \fBovs\-dpctl\fR command sequence for controlling an
+OpenVSwitch kernel module:
+
+.TP
+\fBovs\-dpctl adddp dp0\fR
+Creates datapath number 0.
+
+.TP
+\fBovs\-dpctl addif dp0 eth0 eth1\fR
+Adds two network devices to the new datapath.
+
+.PP
+At this point one would ordinarily start \fBsecchan\fR(8) on
+\fBdp0\fR, transforming \fBdp0\fR into an OpenFlow switch.  Then, when
+the switch and the datapath is no longer needed:
+
+.TP
+\fBovs\-dpctl delif dp0 eth0 eth1\fR
+Removes network devices from the datapath.
+
+.TP
+\fBovs\-dpctl deldp dp0\fR
+Deletes the datapath.
+
+.SH "SEE ALSO"
+
+.BR vswitchd (8),
+.BR secchan (8),
+.BR ovs\-appctl (8)
diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
new file mode 100644 (file)
index 0000000..a8c7b1b
--- /dev/null
@@ -0,0 +1,593 @@
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * 
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include <config.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "command-line.h"
+#include "compiler.h"
+#include "dirs.h"
+#include "dpif.h"
+#include "dynamic-string.h"
+#include "netdev.h"
+#include "odp-util.h"
+#include "timeval.h"
+#include "util.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_dpctl
+
+struct command {
+    const char *name;
+    int min_args;
+    int max_args;
+    void (*handler)(int argc, char *argv[]);
+};
+
+static struct command all_commands[];
+
+static void usage(void) NO_RETURN;
+static void parse_options(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+    struct command *p;
+
+    set_program_name(argv[0]);
+    time_init();
+    vlog_init();
+    parse_options(argc, argv);
+    signal(SIGPIPE, SIG_IGN);
+
+    argc -= optind;
+    argv += optind;
+    if (argc < 1)
+        ovs_fatal(0, "missing command name; use --help for help");
+
+    for (p = all_commands; p->name != NULL; p++) {
+        if (!strcmp(p->name, argv[0])) {
+            int n_arg = argc - 1;
+            if (n_arg < p->min_args)
+                ovs_fatal(0, "'%s' command requires at least %d arguments",
+                          p->name, p->min_args);
+            else if (n_arg > p->max_args)
+                ovs_fatal(0, "'%s' command takes at most %d arguments",
+                          p->name, p->max_args);
+            else {
+                p->handler(argc, argv);
+                if (ferror(stdout)) {
+                    ovs_fatal(0, "write to stdout failed");
+                }
+                if (ferror(stderr)) {
+                    ovs_fatal(0, "write to stderr failed");
+                }
+                exit(0);
+            }
+        }
+    }
+    ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]);
+
+    return 0;
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+    static struct option long_options[] = {
+        {"timeout", required_argument, 0, 't'},
+        {"verbose", optional_argument, 0, 'v'},
+        {"help", no_argument, 0, 'h'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+    };
+    char *short_options = long_options_to_short_options(long_options);
+
+    for (;;) {
+        unsigned long int timeout;
+        int c;
+
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 't':
+            timeout = strtoul(optarg, NULL, 10);
+            if (timeout <= 0) {
+                ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
+                          optarg);
+            } else {
+                time_alarm(timeout);
+            }
+            break;
+
+        case 'h':
+            usage();
+
+        case 'V':
+            printf("%s %s compiled "__DATE__" "__TIME__"\n",
+                   program_name, VERSION BUILDNR);
+            exit(EXIT_SUCCESS);
+
+        case 'v':
+            vlog_set_verbosity(optarg);
+            break;
+
+        case '?':
+            exit(EXIT_FAILURE);
+
+        default:
+            abort();
+        }
+    }
+    free(short_options);
+}
+
+static void
+usage(void)
+{
+    printf("%s: OpenVSwitch datapath management utility\n"
+           "usage: %s [OPTIONS] COMMAND [ARG...]\n"
+           "  adddp DP [IFACE...]         add new datapath DP (with IFACES)\n"
+           "  deldp DP                    delete local datapath DP\n"
+           "  addif DP IFACE...           add each IFACE as a port on DP\n"
+           "  delif DP IFACE...           delete each IFACE from DP\n"
+           "  dp-show                     show basic info on all datapaths\n"
+           "  dp-show DP...               show basic info on each DP\n"
+           "  dp-dump-flows DP            display flows in DP\n"
+           "  dp-del-flows DP             delete all flows from DP\n"
+           "  dp-dump-groups DP           display port groups in DP\n",
+           program_name, program_name);
+    vlog_usage();
+    printf("\nOther options:\n"
+           "  -t, --timeout=SECS          give up after SECS seconds\n"
+           "  -h, --help                  display this help message\n"
+           "  -V, --version               display version information\n");
+    exit(EXIT_SUCCESS);
+}
+
+static void run(int retval, const char *message, ...)
+    PRINTF_FORMAT(2, 3);
+
+static void run(int retval, const char *message, ...)
+{
+    if (retval) {
+        va_list args;
+
+        fprintf(stderr, "%s: ", program_name);
+        va_start(args, message);
+        vfprintf(stderr, message, args);
+        va_end(args);
+        if (retval == EOF) {
+            fputs(": unexpected end of file\n", stderr);
+        } else {
+            fprintf(stderr, ": %s\n", strerror(retval));
+        }
+
+        exit(EXIT_FAILURE);
+    }
+}
+\f
+static void do_add_port(int argc, char *argv[]);
+
+static int if_up(const char *netdev_name)
+{
+    struct netdev *netdev;
+    int retval;
+
+    retval = netdev_open(netdev_name, NETDEV_ETH_TYPE_NONE, &netdev);
+    if (!retval) {
+        retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
+        netdev_close(netdev);
+    }
+    return retval;
+}
+
+static void
+do_get_idx(int argc UNUSED, char *argv[])
+{
+    struct dpif dpif;
+    run(dpif_open(argv[1], &dpif), "opening datapath");
+    printf("%u\n", dpif_id(&dpif));
+    dpif_close(&dpif);
+}
+
+static void
+do_get_name(int argc UNUSED, char *argv[])
+{
+    struct dpif dpif;
+    char name[IF_NAMESIZE + 1];
+
+    run(dpif_open(argv[1], &dpif), "opening datapath");
+    run(dpif_get_name(&dpif, name, sizeof name), "getting datapath name");
+    puts(name);
+    dpif_close(&dpif);
+}
+
+static void
+do_add_dp(int argc UNUSED, char *argv[])
+{
+    struct dpif dpif;
+    run(dpif_create(argv[1], &dpif), "add_dp");
+    dpif_close(&dpif);
+    if (argc > 2) {
+        do_add_port(argc, argv);
+    }
+}
+
+static void
+do_del_dp(int argc UNUSED, char *argv[])
+{
+    struct dpif dpif;
+    run(dpif_open(argv[1], &dpif), "opening datapath");
+    run(dpif_delete(&dpif), "del_dp");
+    dpif_close(&dpif);
+}
+
+static int
+compare_ports(const void *a_, const void *b_)
+{
+    const struct odp_port *a = a_;
+    const struct odp_port *b = b_;
+    return a->port < b->port ? -1 : a->port > b->port;
+}
+
+static void
+query_ports(struct dpif *dpif, struct odp_port **ports, size_t *n_ports)
+{
+    run(dpif_port_list(dpif, ports, n_ports), "listing ports");
+    qsort(*ports, *n_ports, sizeof **ports, compare_ports);
+}
+
+static uint16_t
+get_free_port(struct dpif *dpif)
+{
+    struct odp_port *ports;
+    size_t n_ports;
+    int port_no;
+
+    query_ports(dpif, &ports, &n_ports);
+    for (port_no = 0; port_no <= UINT16_MAX; port_no++) {
+        size_t i;
+        for (i = 0; i < n_ports; i++) {
+            if (ports[i].port == port_no) {
+                goto next_portno;
+            }
+        }
+        free(ports);
+        return port_no;
+
+    next_portno: ;
+    }
+    ovs_fatal(0, "no free datapath ports");
+}
+
+static void
+do_add_port(int argc UNUSED, char *argv[])
+{
+    bool failure = false;
+    struct dpif dpif;
+    int i;
+
+    run(dpif_open(argv[1], &dpif), "opening datapath");
+    for (i = 2; i < argc; i++) {
+        char *save_ptr = NULL;
+        char *devname, *suboptions;
+        int port = -1;
+        int flags = 0;
+        int error;
+
+        devname = strtok_r(argv[i], ",,", &save_ptr);
+        if (!devname) {
+            ovs_error(0, "%s is not a valid network device name", argv[i]);
+            continue;
+        }
+
+        suboptions = strtok_r(NULL, "", &save_ptr);
+        if (suboptions) {
+            enum {
+                AP_PORT,
+                AP_INTERNAL
+            };
+            static char *options[] = {
+                "port",
+                "internal"
+            };
+
+            while (*suboptions != '\0') {
+                char *value;
+
+                switch (getsubopt(&suboptions, options, &value)) {
+                case AP_PORT:
+                    if (!value) {
+                        ovs_error(0, "'port' suboption requires a value");
+                    }
+                    port = atoi(value);
+                    break;
+
+                case AP_INTERNAL:
+                    flags |= ODP_PORT_INTERNAL;
+                    break;
+
+                default:
+                    ovs_error(0, "unknown suboption '%s'", value);
+                    break;
+                }
+            }
+        }
+        if (port < 0) {
+            port = get_free_port(&dpif);
+        }
+
+        error = dpif_port_add(&dpif, devname, port, flags);
+        if (error) {
+            ovs_error(error, "adding %s as port %"PRIu16" of %s failed",
+                      devname, port, argv[1]);
+            failure = true;
+        } else if (if_up(devname)) {
+            failure = true;
+        }
+    }
+    dpif_close(&dpif);
+    if (failure) {
+        exit(EXIT_FAILURE);
+    }
+}
+
+static bool
+get_port_number(struct dpif *dpif, const char *name, uint16_t *port)
+{
+    struct odp_port *ports;
+    size_t n_ports;
+    size_t i;
+
+    query_ports(dpif, &ports, &n_ports);
+    for (i = 0; i < n_ports; i++) {
+        if (!strcmp(name, ports[i].devname)) {
+            *port = ports[i].port;
+            free(ports);
+            return true;
+        }
+    }
+    free(ports);
+    ovs_error(0, "no port named %s", name);
+    return false;
+}
+
+static void
+do_del_port(int argc UNUSED, char *argv[])
+{
+    bool failure = false;
+    struct dpif dpif;
+    int i;
+
+    run(dpif_open(argv[1], &dpif), "opening datapath");
+    for (i = 2; i < argc; i++) {
+        const char *name = argv[i];
+        uint16_t port;
+        int error;
+
+        if (!name[strspn(name, "0123456789")]) {
+            port = atoi(name);
+        } else if (!get_port_number(&dpif, name, &port)) {
+            failure = true;
+            continue;
+        }
+
+        error = dpif_port_del(&dpif, port);
+        if (error) {
+            ovs_error(error, "deleting port %s from %s failed", name, argv[1]);
+            failure = true;
+        }
+    }
+    dpif_close(&dpif);
+    if (failure) {
+        exit(EXIT_FAILURE);
+    }
+}
+
+static void
+show_dpif(struct dpif *dpif)
+{
+    struct odp_port *ports;
+    struct odp_stats stats;
+    size_t n_ports;
+    size_t i;
+
+    printf("dp%u:\n", dpif_id(dpif));
+    if (!dpif_get_dp_stats(dpif, &stats)) {
+        printf("\tflows: cur:%"PRIu32", soft-max:%"PRIu32", "
+               "hard-max:%"PRIu32"\n",
+               stats.n_flows, stats.cur_capacity, stats.max_capacity);
+        printf("\tports: cur:%"PRIu32", max:%"PRIu32"\n",
+               stats.n_ports, stats.max_ports);
+        printf("\tgroups: max:%"PRIu16"\n", stats.max_groups);
+        printf("\tlookups: frags:%"PRIu64", hit:%"PRIu64", missed:%"PRIu64", "
+               "lost:%"PRIu64"\n",
+               stats.n_frags, stats.n_hit, stats.n_missed, stats.n_lost);
+        printf("\tqueues: max-miss:%"PRIu16", max-action:%"PRIu16"\n",
+               stats.max_miss_queue, stats.max_action_queue);
+    }
+    query_ports(dpif, &ports, &n_ports);
+    for (i = 0; i < n_ports; i++) {
+        printf("\tport %u: %s", ports[i].port, ports[i].devname);
+        if (ports[i].flags & ODP_PORT_INTERNAL) {
+            printf(" (internal)");
+        }
+        printf("\n");
+    }
+    free(ports);
+    dpif_close(dpif);
+}
+
+static void
+do_dp_show(int argc UNUSED, char *argv[])
+{
+    bool failure = false;
+    if (argc > 1) {
+        int i;
+        for (i = 1; i < argc; i++) {
+            const char *name = argv[i];
+            struct dpif dpif;
+            int error;
+
+            error = dpif_open(name, &dpif);
+            if (!error) {
+                show_dpif(&dpif);
+            } else {
+                ovs_error(error, "opening datapath %s failed", name);
+                failure = true;
+            }
+        }
+    } else {
+        unsigned int i;
+        for (i = 0; i < ODP_MAX; i++) {
+            char name[128];
+            struct dpif dpif;
+            int error;
+
+            sprintf(name, "dp%u", i);
+            error = dpif_open(name, &dpif);
+            if (!error) {
+                show_dpif(&dpif);
+            } else if (error != ENODEV) {
+                ovs_error(error, "opening datapath %s failed", name);
+                failure = true;
+            }
+        }
+    }
+    if (failure) {
+        exit(EXIT_FAILURE);
+    }
+}
+
+static void
+do_dp_dump_flows(int argc UNUSED, char *argv[])
+{
+    struct odp_flow *flows;
+    struct dpif dpif;
+    size_t n_flows;
+    struct ds ds;
+    size_t i;
+
+    run(dpif_open(argv[1], &dpif), "opening datapath");
+    run(dpif_flow_list_all(&dpif, &flows, &n_flows), "listing all flows");
+
+    ds_init(&ds);
+    for (i = 0; i < n_flows; i++) {
+        struct odp_flow *f = &flows[i];
+        enum { MAX_ACTIONS = 4096 / sizeof(union odp_action) };
+        union odp_action actions[MAX_ACTIONS];
+
+        f->actions = actions;
+        f->n_actions = MAX_ACTIONS;
+        dpif_flow_get(&dpif, f);
+
+        ds_clear(&ds);
+        format_odp_flow(&ds, f);
+        printf("%s\n", ds_cstr(&ds));
+    }
+    ds_destroy(&ds);
+    dpif_close(&dpif);
+}
+
+static void
+do_dp_del_flows(int argc UNUSED, char *argv[])
+{
+    struct dpif dpif;
+
+    run(dpif_open(argv[1], &dpif), "opening datapath");
+    run(dpif_flow_flush(&dpif), "deleting all flows");
+    dpif_close(&dpif);
+}
+
+static void
+do_dp_dump_groups(int argc UNUSED, char *argv[])
+{
+    struct odp_stats stats;
+    struct dpif dpif;
+    unsigned int i;
+
+    run(dpif_open(argv[1], &dpif), "opening datapath");
+    run(dpif_get_dp_stats(&dpif, &stats), "get datapath stats");
+    for (i = 0; i < stats.max_groups; i++) {
+        uint16_t ports[UINT16_MAX];
+        size_t n_ports;
+
+        if (!dpif_port_group_get(&dpif, i, ports,
+                                 ARRAY_SIZE(ports), &n_ports) && n_ports) {
+            size_t j;
+
+            printf("group %u:", i);
+            for (j = 0; j < n_ports; j++) {
+                printf(" %"PRIu16, ports[j]);
+            }
+            printf("\n");
+        }
+    }
+    dpif_close(&dpif);
+}
+
+static void
+do_help(int argc UNUSED, char *argv[] UNUSED)
+{
+    usage();
+}
+
+static struct command all_commands[] = {
+    { "adddp", 1, INT_MAX, do_add_dp },
+    { "deldp", 1, 1, do_del_dp },
+    { "addif", 2, INT_MAX, do_add_port },
+    { "delif", 2, INT_MAX, do_del_port },
+    { "get-idx", 1, 1, do_get_idx },
+    { "get-name", 1, 1, do_get_name },
+    { "dp-show", 0, INT_MAX, do_dp_show },
+    { "dp-dump-flows", 1, 1, do_dp_dump_flows },
+    { "dp-del-flows", 1, 1, do_dp_del_flows },
+    { "dp-dump-groups", 1, 1, do_dp_dump_groups },
+    { "help", 0, INT_MAX, do_help },
+    { NULL, 0, 0, NULL },
+};
index 07a56c33fa5957ef3f97479cd197ddde9de8be58..4e09861238cb42e5da73214311dad17cf557f32b 100755 (executable)
@@ -99,7 +99,7 @@ while `/bin/true`; do
         if [ -d /proc/$pid ]; then
             # Check if the secchan and datapath still can communicate
             if [ -S $SECCHAN_SOCK ]; then
-                dpctl probe -t 2 unix:$SECCHAN_SOCK 
+                ovs-ofctl probe -t 2 unix:$SECCHAN_SOCK 
                 if [ $? -ne 0 ]; then
                     log "datapath probe failed"
                     let DP_DOWN++
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
new file mode 100644 (file)
index 0000000..1243f04
--- /dev/null
@@ -0,0 +1,489 @@
+.TH ovs\-ofctl 8 "March 2009" "OpenVSwitch" "OpenVSwitch Manual"
+.ds PN ovs\-ofctl
+
+.SH NAME
+ovs\-ofctl \- administer OpenFlow switches
+
+.SH SYNOPSIS
+.B ovs\-ofctl
+[\fIoptions\fR] \fIcommand \fR[\fIswitch\fR] [\fIargs\fR\&...]
+
+.SH DESCRIPTION
+The
+.B ovs\-ofctl
+program is a command line tool for monitoring and administering
+OpenFlow switches.  It can also show the current state of an OpenFlow
+switch, including features, configuration, and table entries.
+
+.SS "OpenFlow Switch Management Commands"
+
+These commands allow \fBovs\-ofctl\fR to monitor and administer an OpenFlow
+switch.  It is able to show the current state of a switch, including
+features, configuration, and table entries.
+
+Most of these commands take an argument that specifies the method for
+connecting to an OpenFlow switch.  The following connection methods
+are supported:
+
+.RS
+.TP
+\fBssl:\fIhost\fR[\fB:\fIport\fR]
+The specified SSL \fIport\fR (default: 6633) on the given remote
+\fIhost\fR.  The \fB--private-key\fR, \fB--certificate\fR, and
+\fB--ca-cert\fR options are mandatory when this form is used.
+
+.TP
+\fBtcp:\fIhost\fR[\fB:\fIport\fR]
+The specified TCP \fIport\fR (default: 6633) on the given remote
+\fIhost\fR.
+
+.TP
+\fBunix:\fIfile\fR
+The Unix domain server socket named \fIfile\fR.  
+
+.IP "\fIfile\fR"
+This is short for \fBunix:\fIfile\fR, as long as \fIfile\fR does not
+contain a colon.
+
+.IP \fIdp\fR
+This is short for \fBunix:@RUNDIR@/\fIdp\fB.mgmt\fR, as long as
+\fIdp\fR does not contain a colon.
+.RE
+
+.TP
+\fBshow \fIswitch\fR
+Prints to the console information on \fIswitch\fR, including
+information on its flow tables and ports.
+
+.TP
+\fBstatus \fIswitch\fR [\fIkey\fR]
+Prints to the console a series of key-value pairs that report the
+status of \fIswitch\fR.  If \fIkey\fR is specified, only the key-value
+pairs whose key names begin with \fIkey\fR are printed.  If \fIkey\fR is
+omitted, all key-value pairs are printed.
+
+.TP
+\fBdump-tables \fIswitch\fR
+Prints to the console statistics for each of the flow tables used by
+\fIswitch\fR.
+
+.TP
+\fBdump-ports \fIswitch\fR
+Prints to the console statistics for each of the network devices
+associated with \fIswitch\fR.
+
+.TP
+\fBmod-port \fIswitch\fR \fInetdev\fR \fIaction\fR
+Modify characteristics of an interface monitored by \fIswitch\fR.  
+\fInetdev\fR can be referred to by its OpenFlow assigned port number or 
+the device name, e.g. \fBeth0\fR.  The \fIaction\fR may be any one of the
+following:
+
+.RS
+.IP \fBup\fR
+Enables the interface.  This is equivalent to ``ifconfig up'' on a Unix
+system.
+
+.IP \fBdown\fR
+Disables the interface.  This is equivalent to ``ifconfig down'' on a Unix
+system.
+
+.IP \fBflood\fR
+When a \fIflood\fR action is specified, traffic will be sent out this
+interface.  This is the default posture for monitored ports.
+
+.IP \fBnoflood\fR
+When a \fIflood\fR action is specified, traffic will not be sent out 
+this interface.  This is primarily useful to prevent loops when a
+spanning tree protocol is not in use.
+
+.RE
+
+.TP
+\fBdump-flows \fIswitch \fR[\fIflows\fR]
+Prints to the console all flow entries in \fIswitch\fR's
+tables that match \fIflows\fR.  If \fIflows\fR is omitted, all flows
+in the switch are retrieved.  See \fBFlow Syntax\fR, below, for the
+syntax of \fIflows\fR.  The output format is described in 
+\fBTable Entry Output\fR.
+
+.TP
+\fBdump-aggregate \fIswitch \fR[\fIflows\fR]
+Prints to the console aggregate statistics for flows in 
+\fIswitch\fR's tables that match \fIflows\fR.  If \fIflows\fR is omitted, 
+the statistics are aggregated across all flows in the switch's flow
+tables.  See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR.
+The output format is descrbed in \fBTable Entry Output\fR.
+
+.TP
+\fBadd-flow \fIswitch flow\fR
+Add the flow entry as described by \fIflow\fR to the \fIswitch\fR's 
+tables.  The flow entry is in the format described in \fBFlow Syntax\fR, 
+below.
+
+.TP
+\fBadd-flows \fIswitch file\fR
+Add flow entries as described in \fIfile\fR to \fIswitch\fR's 
+tables.  Each line in \fIfile\fR is a flow entry in the format
+described in \fBFlow Syntax\fR, below.
+
+.TP
+\fBmod-flows \fIswitch flow\fR
+Modify the actions in entries from the \fIswitch\fR's tables 
+that match \fIflow\fR.  When invoked with the \fB--strict\fR option,
+wildcards are not treated as active for matching purposes.  See 
+\fBFlow Syntax\fR, below, for the syntax of \fIflows\fR.
+
+.TP
+\fBdel-flows \fIswitch \fR[\fIflow\fR]
+Deletes entries from the \fIswitch\fR's tables that match
+\fIflow\fR.  When invoked with the \fB--strict\fR option, wildcards are 
+not treated as active for matching purposes.  If \fIflow\fR is 
+omitted and the \fB--strict\fR option is not used, all flows in the 
+switch's tables are removed.  See \fBFlow Syntax\fR, below, for the 
+syntax of \fIflows\fR.
+
+.TP
+\fBmonitor \fIswitch\fR [\fImiss-len\fR [\fIsend-exp]]
+Connects to \fIswitch\fR and prints to the console all OpenFlow
+messages received.  Usually, \fIswitch\fR should specify a connection
+named on \fBsecchan\fR(8)'s \fB-l\fR or \fB--listen\fR command line
+option.
+
+If \fImiss-len\fR is provided, \fBovs\-ofctl\fR sends an OpenFlow ``set
+configuration'' message at connection setup time that requests
+\fImiss-len\fR bytes of each packet that misses the flow table.  The
+OpenFlow reference implementation not send these messages to the
+\fBovs\-ofctl monitor\fR client connection unless a nonzero value is
+specified on this argument.
+
+If \fIsend-exp\fR is specified as \fB1\fR, \fBovs\-ofctl\fR will also
+request to be sent flow expiration messages.  If this argument is
+omitted, or \fB0\fR is specified, then \fRovs\-ofctl\fR will not request
+flow expirations.
+
+This command may be useful for debugging switch or controller
+implementations.
+
+.TP
+\fBexecute \fIswitch command \fR[\fIarg\fR...]
+Sends a request to \fIswitch\fR to execute \fIcommand\fR along with
+each \fIarg\fR, if any, then waits for the command to complete and
+reports its completion status on \fBstderr\fR and its output, if any,
+on \fBstdout\fR.  The set of available commands and their argument is
+switch-dependent.  (This command uses a Nicira extension to OpenFlow
+that may not be available on all switches.)
+
+.SS "OpenFlow Switch and Controller Commands"
+
+The following commands, like those in the previous section, may be
+applied to OpenFlow switches, using any of the connection methods
+described in that section.  Unlike those commands, these may also be
+applied to OpenFlow controllers.
+
+.TP
+\fBprobe \fItarget\fR
+Sends a single OpenFlow echo-request message to \fItarget\fR and waits
+for the response.  With the \fB-t\fR or \fB--timeout\fR option, this
+command can test whether an OpenFlow switch or controller is up and
+running.
+
+.TP
+\fBping \fItarget \fR[\fIn\fR]
+Sends a series of 10 echo request packets to \fItarget\fR and times
+each reply.  The echo request packets consist of an OpenFlow header
+plus \fIn\fR bytes (default: 64) of randomly generated payload.  This
+measures the latency of individual requests.
+
+.TP
+\fBbenchmark \fItarget n count\fR
+Sends \fIcount\fR echo request packets that each consist of an
+OpenFlow header plus \fIn\fR bytes of payload and waits for each
+response.  Reports the total time required.  This is a measure of the
+maximum bandwidth to \fItarget\fR for round-trips of \fIn\fR-byte
+messages.
+
+.SS "Flow Syntax"
+
+Some \fBovs\-ofctl\fR commands accept an argument that describes a flow or
+flows.  Such flow descriptions comprise a series
+\fIfield\fB=\fIvalue\fR assignments, separated by commas or white
+space.  (Embedding spaces into a flow description normally requires
+quoting to prevent the shell from breaking the description into
+multiple arguments.)
+
+The following field assignments describe how a flow matches a packet.
+If any of these assignments is omitted from the flow syntax, the field
+is treated as a wildcard; thus, if all of them are omitted, the
+resulting flow matches all packets.  The string \fB*\fR or \fBANY\fR
+may be specified to explicitly mark any of these fields as a wildcard.  
+(\fB*\fR should be quoted to protect it from shell expansion.)
+
+.IP \fBin_port=\fIport_no\fR
+Matches physical port \fIport_no\fR.  Switch ports are numbered as
+displayed by \fBovs\-ofctl show\fR.
+
+.IP \fBdl_vlan=\fIvlan\fR
+Matches IEEE 802.1q virtual LAN tag \fIvlan\fR.  Specify \fB0xffff\fR
+as \fIvlan\fR to match packets that are not tagged with a virtual LAN;
+otherwise, specify a number between 0 and 4095, inclusive, as the
+12-bit VLAN ID to match.
+
+.IP \fBdl_src=\fImac\fR
+Matches Ethernet source address \fImac\fR, which is specified as 6 pairs 
+of hexadecimal digits delimited by colons (e.g. \fB00:0A:E4:25:6B:B0\fR).
+
+.IP \fBdl_dst=\fImac\fR
+Matches Ethernet destination address \fImac\fR.
+
+.IP \fBdl_type=\fIethertype\fR
+Matches Ethernet protocol type \fIethertype\fR, which is specified as an
+integer between 0 and 65535, inclusive, either in decimal or as a 
+hexadecimal number prefixed by \fB0x\fR (e.g. \fB0x0806\fR to match ARP 
+packets).
+
+.IP \fBnw_src=\fIip\fR[\fB/\fInetmask\fR]
+Matches IPv4 source address \fIip\fR, which may be specified as an
+IP address or host name (e.g. \fB192.168.1.1\fR or
+\fBwww.example.com\fR).  The optional \fInetmask\fR allows restricting a
+match to an IPv4 address prefix.  The netmask may be specified as a dotted 
+quad (e.g. \fB192.168.1.0/255.255.255.0\fR) or as a CIDR block 
+(e.g. \fB192.168.1.0/24\fR).
+
+.IP \fBnw_dst=\fIip\fR[\fB/\fInetmask\fR]
+Matches IPv4 destination address \fIip\fR.
+
+.IP \fBnw_proto=\fIproto\fR
+Matches IP protocol type \fIproto\fR, which is specified as a decimal 
+number between 0 and 255, inclusive (e.g. 6 to match TCP packets).
+
+.IP \fBtp_src=\fIport\fR
+Matches UDP or TCP source port \fIport\fR, which is specified as a decimal 
+number between 0 and 65535, inclusive (e.g. 80 to match packets originating 
+from a HTTP server).
+
+.IP \fBtp_dst=\fIport\fR
+Matches UDP or TCP destination port \fIport\fR.
+
+.IP \fBicmp_type=\fItype\fR
+Matches ICMP message with \fItype\fR, which is specified as a decimal 
+number between 0 and 255, inclusive.
+
+.IP \fBicmp_code=\fIcode\fR
+Matches ICMP messages with \fIcode\fR.
+
+.PP
+The following shorthand notations are also available:
+
+.IP \fBip\fR
+Same as \fBdl_type=0x0800\fR.
+
+.IP \fBicmp\fR
+Same as \fBdl_type=0x0800,nw_proto=1\fR.
+
+.IP \fBtcp\fR
+Same as \fBdl_type=0x0800,nw_proto=6\fR.
+
+.IP \fBudp\fR
+Same as \fBdl_type=0x0800,nw_proto=17\fR.
+
+.IP \fBarp\fR
+Same as \fBdl_type=0x0806\fR.
+
+.PP
+The \fBadd-flow\fR and \fBadd-flows\fR commands require an additional field:
+
+.IP \fBactions=\fR[\fItarget\fR][\fB,\fItarget\fR...]\fR
+Specifies a comma-separated list of actions to take on a packet when the 
+flow entry matches.  If no \fItarget\fR is specified, then packets
+matching the flow are dropped.  The \fItarget\fR may be a decimal port 
+number designating the physical port on which to output the packet, or one 
+of the following keywords:
+
+.RS
+.IP \fBoutput\fR:\fIport\fR
+Outputs the packet on the port specified by \fIport\fR.
+
+.IP \fBnormal\fR
+Subjects the packet to the device's normal L2/L3 processing.  (This
+action is not implemented by all OpenFlow switches.)
+
+.IP \fBflood\fR
+Outputs the packet on all switch physical ports other than the port on
+which it was received and any ports on which flooding is disabled
+(typically, these would be ports disabled by the IEEE 802.1D spanning
+tree protocol).
+
+.IP \fBall\fR
+Outputs the packet on all switch physical ports other than the port on
+which it was received.
+
+.IP \fBcontroller\fR:\fImax_len\fR
+Sends the packet to the OpenFlow controller as a ``packet in''
+message.  If \fImax_len\fR is a number, then it specifies the maximum
+number of bytes that should be sent.  If \fImax_len\fR is \fBALL\fR or
+omitted, then the entire packet is sent.
+
+.IP \fBlocal\fR
+Outputs the packet on the ``local port,'' which corresponds to the
+\fBof\fIn\fR network device (see \fBCONTACTING THE CONTROLLER\fR in
+\fBsecchan\fR(8) for information on the \fBof\fIn\fR network device).
+
+.IP \fBdrop\fR
+Discards the packet, so no further processing or forwarding takes place.
+If a drop action is used, no other actions may be specified.
+
+.IP \fBmod_vlan_vid\fR:\fIvlan_vid\fR
+Modifies the VLAN id on a packet.  The VLAN tag is added or modified 
+as necessary to match the value specified.  If the VLAN tag is added,
+a priority of zero is used (see the \fBmod_vlan_pcp\fR action to set
+this).
+
+.IP \fBmod_vlan_pcp\fR:\fIvlan_pcp\fR
+Modifies the VLAN priority on a packet.  The VLAN tag is added or modified 
+as necessary to match the value specified.  Valid values are between 0
+(lowest) and 7 (highest).  If the VLAN tag is added, a vid of zero is used 
+(see the \fBmod_vlan_vid\fR action to set this).
+
+.IP \fBstrip_vlan\fR
+Strips the VLAN tag from a packet if it is present.
+
+.IP \fBmod_dl_src\fB:\fImac\fR
+Sets the source Ethernet address to \fImac\fR.
+
+.IP \fBmod_dl_dst\fB:\fImac\fR
+Sets the destination Ethernet address to \fImac\fR.
+.RE
+
+.IP
+(The OpenFlow protocol supports other actions that \fBovs\-ofctl\fR does
+not yet expose to the user.)
+
+.PP
+The \fBadd-flow\fR, \fBadd-flows\fR, and \fBdel-flows\fR commands
+support an additional optional field:
+
+.IP \fBpriority=\fIvalue\fR
+The priority at which a wildcarded entry will match in comparison to
+others.  \fIvalue\fR is a number between 0 and 65535, inclusive.  A higher 
+\fIvalue\fR will match before a lower one.  An exact-match entry will always 
+have priority over an entry containing wildcards, so it has an implicit 
+priority value of 65535.  When adding a flow, if the field is not specified, 
+the flow's priority will default to 32768.
+
+.PP
+The \fBadd-flow\fR and \fBadd-flows\fR commands support additional
+optional fields:
+
+.TP
+\fBidle_timeout=\fIseconds\fR
+Causes the flow to expire after the given number of seconds of
+inactivity.  A value of 0 prevents a flow from expiring due to
+inactivity.  The default is 60 seconds.
+
+.IP \fBhard_timeout=\fIseconds\fR
+Causes the flow to expire after the given number of seconds,
+regardless of activity.  A value of 0 (the default) gives the flow no
+hard expiration deadline.
+
+.PP
+The \fBdump-flows\fR, \fBdump-aggregate\fR, \fBdel-flow\fR 
+and \fBdel-flows\fR commands support one additional optional field:
+
+.TP
+\fBout_port=\fIport\fR
+If set, a matching flow must include an output action to \fIport\fR.
+
+.PP
+The \fBdump-flows\fR and \fBdump-aggregate\fR commands support an
+additional optional field:
+
+.IP \fBtable=\fInumber\fR
+If specified, limits the flows about which statistics are gathered to
+those in the table with the given \fInumber\fR.  Tables are numbered
+as shown by the \fBdump-tables\fR command.
+
+If this field is not specified, or if \fInumber\fR is given as
+\fB255\fR, statistics are gathered about flows from all tables.
+
+.SS "Table Entry Output"
+
+The \fBdump-tables\fR and \fBdump-aggregate\fR commands print information 
+about the entries in a datapath's tables.  Each line of output is a 
+unique flow entry, which begins with some common information:
+
+.IP \fBduration\fR
+The number of seconds the entry has been in the table.
+
+.IP \fBtable_id\fR
+The table that contains the flow.  When a packet arrives, the switch 
+begins searching for an entry at the lowest numbered table.  Tables are 
+numbered as shown by the \fBdump-tables\fR command.
+
+.IP \fBpriority\fR
+The priority of the entry in relation to other entries within the same
+table.  A higher value will match before a lower one.
+
+.IP \fBn_packets\fR
+The number of packets that have matched the entry.
+
+.IP \fBn_bytes\fR
+The total number of bytes from packets that have matched the entry.
+
+.PP
+The rest of the line consists of a description of the flow entry as 
+described in \fBFlow Syntax\fR, above.
+
+
+.SH OPTIONS
+.TP
+\fB--strict\fR
+Uses strict matching when running flow modification commands.
+
+.TP
+\fB-t\fR, \fB--timeout=\fIsecs\fR
+Limits \fBovs\-ofctl\fR runtime to approximately \fIsecs\fR seconds.  If
+the timeout expires, \fBovs\-ofctl\fR will exit with a \fBSIGALRM\fR
+signal.
+
+.TP
+\fB-p\fR, \fB--private-key=\fIprivkey.pem\fR
+Specifies a PEM file containing the private key used as the
+identity for SSL connections to a switch.
+
+.TP
+\fB-c\fR, \fB--certificate=\fIcert.pem\fR
+Specifies a PEM file containing a certificate, signed by the
+controller's certificate authority (CA), that certifies the
+private key to identify a trustworthy controller.
+
+.TP
+\fB-C\fR, \fB--ca-cert=\fIcacert.pem\fR
+Specifies a PEM file containing the CA certificate used to verify that
+a switch is trustworthy.
+
+.so lib/vlog.man
+.so lib/common.man
+
+.SH EXAMPLES
+
+The following examples assume that an OpenFlow switch on the local
+host has been configured to listen for management connections on a
+Unix domain socket named \fB@RUNDIR@/openflow.sock\fR, e.g. by
+specifying \fB--listen=punix:@RUNDIR@/openflow.sock\fR on the
+\fBsecchan\fR(8) command line.
+
+.TP
+\fBovs\-ofctl dump-tables unix:@RUNDIR@/openflow.sock\fR
+Prints out the switch's table stats.  (This is more interesting after
+some traffic has passed through.)
+
+.TP
+\fBovs\-ofctl dump-flows unix:@RUNDIR@/openflow.sock\fR
+Prints the flow entries in the switch.
+
+.SH "SEE ALSO"
+
+.BR vswitchd (8),
+.BR ovs\-appctl (8),
+.BR ovs\-controller (8)
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
new file mode 100644 (file)
index 0000000..e5acffc
--- /dev/null
@@ -0,0 +1,1371 @@
+/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * 
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include <config.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "command-line.h"
+#include "compiler.h"
+#include "dirs.h"
+#include "dpif.h"
+#include "dynamic-string.h"
+#include "netdev.h"
+#include "netlink.h"
+#include "odp-util.h"
+#include "ofp-print.h"
+#include "ofpbuf.h"
+#include "openflow/nicira-ext.h"
+#include "openflow/openflow.h"
+#include "packets.h"
+#include "random.h"
+#include "socket-util.h"
+#include "timeval.h"
+#include "util.h"
+#include "vconn-ssl.h"
+#include "vconn.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_ofctl
+
+#define DEFAULT_IDLE_TIMEOUT 60
+
+#define MOD_PORT_CMD_UP      "up"
+#define MOD_PORT_CMD_DOWN    "down"
+#define MOD_PORT_CMD_FLOOD   "flood"
+#define MOD_PORT_CMD_NOFLOOD "noflood"
+
+
+/* Settings that may be configured by the user. */
+struct settings {
+    bool strict;        /* Use strict matching for flow mod commands */
+};
+
+struct command {
+    const char *name;
+    int min_args;
+    int max_args;
+    void (*handler)(const struct settings *, int argc, char *argv[]);
+};
+
+static struct command all_commands[];
+
+static void usage(void) NO_RETURN;
+static void parse_options(int argc, char *argv[], struct settings *);
+
+int main(int argc, char *argv[])
+{
+    struct settings s;
+    struct command *p;
+
+    set_program_name(argv[0]);
+    time_init();
+    vlog_init();
+    parse_options(argc, argv, &s);
+    signal(SIGPIPE, SIG_IGN);
+
+    argc -= optind;
+    argv += optind;
+    if (argc < 1)
+        ovs_fatal(0, "missing command name; use --help for help");
+
+    for (p = all_commands; p->name != NULL; p++) {
+        if (!strcmp(p->name, argv[0])) {
+            int n_arg = argc - 1;
+            if (n_arg < p->min_args)
+                ovs_fatal(0, "'%s' command requires at least %d arguments",
+                          p->name, p->min_args);
+            else if (n_arg > p->max_args)
+                ovs_fatal(0, "'%s' command takes at most %d arguments",
+                          p->name, p->max_args);
+            else {
+                p->handler(&s, argc, argv);
+                if (ferror(stdout)) {
+                    ovs_fatal(0, "write to stdout failed");
+                }
+                if (ferror(stderr)) {
+                    ovs_fatal(0, "write to stderr failed");
+                }
+                exit(0);
+            }
+        }
+    }
+    ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]);
+
+    return 0;
+}
+
+static void
+parse_options(int argc, char *argv[], struct settings *s)
+{
+    enum {
+        OPT_STRICT = UCHAR_MAX + 1
+    };
+    static struct option long_options[] = {
+        {"timeout", required_argument, 0, 't'},
+        {"verbose", optional_argument, 0, 'v'},
+        {"strict", no_argument, 0, OPT_STRICT},
+        {"help", no_argument, 0, 'h'},
+        {"version", no_argument, 0, 'V'},
+        VCONN_SSL_LONG_OPTIONS
+        {0, 0, 0, 0},
+    };
+    char *short_options = long_options_to_short_options(long_options);
+
+    /* Set defaults that we can figure out before parsing options. */
+    s->strict = false;
+
+    for (;;) {
+        unsigned long int timeout;
+        int c;
+
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 't':
+            timeout = strtoul(optarg, NULL, 10);
+            if (timeout <= 0) {
+                ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
+                          optarg);
+            } else {
+                time_alarm(timeout);
+            }
+            break;
+
+        case 'h':
+            usage();
+
+        case 'V':
+            printf("%s %s compiled "__DATE__" "__TIME__"\n",
+                   program_name, VERSION BUILDNR);
+            exit(EXIT_SUCCESS);
+
+        case 'v':
+            vlog_set_verbosity(optarg);
+            break;
+
+        case OPT_STRICT:
+            s->strict = true;
+            break;
+
+        VCONN_SSL_OPTION_HANDLERS
+
+        case '?':
+            exit(EXIT_FAILURE);
+
+        default:
+            abort();
+        }
+    }
+    free(short_options);
+}
+
+static void
+usage(void)
+{
+    printf("%s: OpenFlow switch management utility\n"
+           "usage: %s [OPTIONS] COMMAND [ARG...]\n"
+           "\nFor OpenFlow switches:\n"
+           "  show SWITCH                 show OpenFlow information\n"
+           "  status SWITCH [KEY]         report statistics (about KEY)\n"
+           "  dump-desc SWITCH            print switch description\n"
+           "  dump-tables SWITCH          print table stats\n"
+           "  mod-port SWITCH IFACE ACT   modify port behavior\n"
+           "  dump-ports SWITCH           print port statistics\n"
+           "  dump-flows SWITCH           print all flow entries\n"
+           "  dump-flows SWITCH FLOW      print matching FLOWs\n"
+           "  dump-aggregate SWITCH       print aggregate flow statistics\n"
+           "  dump-aggregate SWITCH FLOW  print aggregate stats for FLOWs\n"
+#ifdef SUPPORT_SNAT
+           "  add-snat SWITCH IFACE IP    add SNAT config to IFACE\n"
+           "  del-snat SWITCH IFACE       delete SNAT config on IFACE\n"
+#endif
+           "  add-flow SWITCH FLOW        add flow described by FLOW\n"
+           "  add-flows SWITCH FILE       add flows from FILE\n"
+           "  mod-flows SWITCH FLOW       modify actions of matching FLOWs\n"
+           "  del-flows SWITCH [FLOW]     delete matching FLOWs\n"
+           "  monitor SWITCH MISSLEN EXP  print packets received from SWITCH\n"
+           "  execute SWITCH CMD [ARG...] execute CMD with ARGS on SWITCH\n"
+           "\nFor OpenFlow switches and controllers:\n"
+           "  probe VCONN                 probe whether VCONN is up\n"
+           "  ping VCONN [N]              latency of N-byte echos\n"
+           "  benchmark VCONN N COUNT     bandwidth of COUNT N-byte echos\n"
+           "where each SWITCH is an active OpenFlow connection method.\n",
+           program_name, program_name);
+    vconn_usage(true, false, false);
+    vlog_usage();
+    printf("\nOther options:\n"
+           "  --strict                    use strict match for flow commands\n"
+           "  -t, --timeout=SECS          give up after SECS seconds\n"
+           "  -h, --help                  display this help message\n"
+           "  -V, --version               display version information\n");
+    exit(EXIT_SUCCESS);
+}
+
+static void run(int retval, const char *message, ...)
+    PRINTF_FORMAT(2, 3);
+
+static void run(int retval, const char *message, ...)
+{
+    if (retval) {
+        va_list args;
+
+        fprintf(stderr, "%s: ", program_name);
+        va_start(args, message);
+        vfprintf(stderr, message, args);
+        va_end(args);
+        if (retval == EOF) {
+            fputs(": unexpected end of file\n", stderr);
+        } else {
+            fprintf(stderr, ": %s\n", strerror(retval));
+        }
+
+        exit(EXIT_FAILURE);
+    }
+}
+\f
+/* Generic commands. */
+
+static void
+open_vconn(const char *name, struct vconn **vconnp)
+{
+    struct dpif dpif;
+    struct stat s;
+
+    if (strstr(name, ":")) {
+        run(vconn_open_block(name, OFP_VERSION, vconnp),
+            "connecting to %s", name);
+    } else if (!stat(name, &s) && S_ISSOCK(s.st_mode)) {
+        char *vconn_name = xasprintf("unix:%s", name);
+        VLOG_INFO("connecting to %s", vconn_name);
+        run(vconn_open_block(vconn_name, OFP_VERSION, vconnp),
+            "connecting to %s", vconn_name);
+        free(vconn_name);
+    } else if (!dpif_open(name, &dpif)) {
+        char dpif_name[IF_NAMESIZE + 1];
+        char *socket_name;
+        char *vconn_name;
+
+        run(dpif_get_name(&dpif, dpif_name, sizeof dpif_name),
+            "obtaining name of %s", dpif_name);
+        dpif_close(&dpif);
+        if (strcmp(dpif_name, name)) {
+            VLOG_INFO("datapath %s is named %s", name, dpif_name);
+        }
+
+        socket_name = xasprintf("%s/%s.mgmt", ovs_rundir, dpif_name);
+        if (stat(socket_name, &s)) {
+            ovs_fatal(errno, "cannot connect to %s: stat failed on %s",
+                      name, socket_name);
+        } else if (!S_ISSOCK(s.st_mode)) {
+            ovs_fatal(0, "cannot connect to %s: %s is not a socket",
+                      name, socket_name);
+        }
+
+        vconn_name = xasprintf("unix:%s", socket_name);
+        VLOG_INFO("connecting to %s", vconn_name);
+        run(vconn_open_block(vconn_name, OFP_VERSION, vconnp),
+            "connecting to %s", vconn_name);
+        free(socket_name);
+        free(vconn_name);
+    } else {
+        ovs_fatal(0, "%s is not a valid connection method", name);
+    }
+}
+
+static void *
+alloc_stats_request(size_t body_len, uint16_t type, struct ofpbuf **bufferp)
+{
+    struct ofp_stats_request *rq;
+    rq = make_openflow((offsetof(struct ofp_stats_request, body)
+                        + body_len), OFPT_STATS_REQUEST, bufferp);
+    rq->type = htons(type);
+    rq->flags = htons(0);
+    return rq->body;
+}
+
+static void
+send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer)
+{
+    update_openflow_length(buffer);
+    run(vconn_send_block(vconn, buffer), "failed to send packet to switch");
+}
+
+static void
+dump_transaction(const char *vconn_name, struct ofpbuf *request)
+{
+    struct vconn *vconn;
+    struct ofpbuf *reply;
+
+    update_openflow_length(request);
+    open_vconn(vconn_name, &vconn);
+    run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
+    ofp_print(stdout, reply->data, reply->size, 1);
+    vconn_close(vconn);
+}
+
+static void
+dump_trivial_transaction(const char *vconn_name, uint8_t request_type)
+{
+    struct ofpbuf *request;
+    make_openflow(sizeof(struct ofp_header), request_type, &request);
+    dump_transaction(vconn_name, request);
+}
+
+static void
+dump_stats_transaction(const char *vconn_name, struct ofpbuf *request)
+{
+    uint32_t send_xid = ((struct ofp_header *) request->data)->xid;
+    struct vconn *vconn;
+    bool done = false;
+
+    open_vconn(vconn_name, &vconn);
+    send_openflow_buffer(vconn, request);
+    while (!done) {
+        uint32_t 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) {
+            struct ofp_stats_reply *osr;
+
+            ofp_print(stdout, reply->data, reply->size, 1);
+
+            osr = ofpbuf_at(reply, 0, sizeof *osr);
+            done = !osr || !(ntohs(osr->flags) & OFPSF_REPLY_MORE);
+        } else {
+            VLOG_DBG("received reply with xid %08"PRIx32" "
+                     "!= expected %08"PRIx32, recv_xid, send_xid);
+        }
+        ofpbuf_delete(reply);
+    }
+    vconn_close(vconn);
+}
+
+static void
+dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
+{
+    struct ofpbuf *request;
+    alloc_stats_request(0, stats_type, &request);
+    dump_stats_transaction(vconn_name, request);
+}
+
+static void
+do_show(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
+    dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
+}
+
+static void
+do_status(const struct settings *s UNUSED, int argc, char *argv[])
+{
+    struct nicira_header *request, *reply;
+    struct vconn *vconn;
+    struct ofpbuf *b;
+
+    request = make_openflow(sizeof *request, OFPT_VENDOR, &b);
+    request->vendor = htonl(NX_VENDOR_ID);
+    request->subtype = htonl(NXT_STATUS_REQUEST);
+    if (argc > 2) {
+        ofpbuf_put(b, argv[2], strlen(argv[2]));
+        update_openflow_length(b);
+    }
+    open_vconn(argv[1], &vconn);
+    run(vconn_transact(vconn, b, &b), "talking to %s", argv[1]);
+    vconn_close(vconn);
+
+    if (b->size < sizeof *reply) {
+        ovs_fatal(0, "short reply (%zu bytes)", b->size);
+    }
+    reply = b->data;
+    if (reply->header.type != OFPT_VENDOR
+        || reply->vendor != ntohl(NX_VENDOR_ID)
+        || reply->subtype != ntohl(NXT_STATUS_REPLY)) {
+        ofp_print(stderr, b->data, b->size, 2);
+        ovs_fatal(0, "bad reply");
+    }
+
+    fwrite(reply + 1, b->size - sizeof *reply, 1, stdout);
+}
+
+static void
+do_dump_desc(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    dump_trivial_stats_transaction(argv[1], OFPST_DESC);
+}
+
+static void
+do_dump_tables(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
+}
+
+
+static uint32_t
+str_to_u32(const char *str) 
+{
+    char *tail;
+    uint32_t value;
+
+    errno = 0;
+    value = strtoul(str, &tail, 0);
+    if (errno == EINVAL || errno == ERANGE || *tail) {
+        ovs_fatal(0, "invalid numeric format %s", str);
+    }
+    return value;
+}
+
+static void
+str_to_mac(const char *str, uint8_t mac[6]) 
+{
+    if (sscanf(str, "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8,
+               &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) {
+        ovs_fatal(0, "invalid mac address %s", str);
+    }
+}
+
+static uint32_t
+str_to_ip(const char *str_, uint32_t *ip)
+{
+    char *str = xstrdup(str_);
+    char *save_ptr = NULL;
+    const char *name, *netmask;
+    struct in_addr in_addr;
+    int n_wild, retval;
+
+    name = strtok_r(str, "//", &save_ptr);
+    retval = name ? lookup_ip(name, &in_addr) : EINVAL;
+    if (retval) {
+        ovs_fatal(0, "%s: could not convert to IP address", str);
+    }
+    *ip = in_addr.s_addr;
+
+    netmask = strtok_r(NULL, "//", &save_ptr);
+    if (netmask) {
+        uint8_t o[4];
+        if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8,
+                   &o[0], &o[1], &o[2], &o[3]) == 4) {
+            uint32_t nm = (o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3];
+            int i;
+
+            /* Find first 1-bit. */
+            for (i = 0; i < 32; i++) {
+                if (nm & (1u << i)) {
+                    break;
+                }
+            }
+            n_wild = i;
+
+            /* Verify that the rest of the bits are 1-bits. */
+            for (; i < 32; i++) {
+                if (!(nm & (1u << i))) {
+                    ovs_fatal(0, "%s: %s is not a valid netmask",
+                              str, netmask);
+                }
+            }
+        } else {
+            int prefix = atoi(netmask);
+            if (prefix <= 0 || prefix > 32) {
+                ovs_fatal(0, "%s: network prefix bits not between 1 and 32",
+                          str);
+            }
+            n_wild = 32 - prefix;
+        }
+    } else {
+        n_wild = 0;
+    }
+
+    free(str);
+    return n_wild;
+}
+
+static void *
+put_action(struct ofpbuf *b, size_t size, uint16_t type)
+{
+    struct ofp_action_header *ah = ofpbuf_put_zeros(b, size);
+    ah->type = htons(type);
+    ah->len = htons(size);
+    return ah;
+}
+
+static struct ofp_action_output *
+put_output_action(struct ofpbuf *b, uint16_t port)
+{
+    struct ofp_action_output *oao = put_action(b, sizeof *oao, OFPAT_OUTPUT);
+    oao->port = htons(port);
+    return oao;
+}
+
+static void
+put_dl_addr_action(struct ofpbuf *b, uint16_t type, const char *addr)
+{
+    struct ofp_action_dl_addr *oada = put_action(b, sizeof *oada, type);
+    str_to_mac(addr, oada->dl_addr);
+}
+
+
+static bool
+parse_port_name(const char *name, uint16_t *port)
+{
+    struct pair {
+        const char *name;
+        uint16_t value;
+    };
+    static const struct pair pairs[] = {
+#define DEF_PAIR(NAME) {#NAME, OFPP_##NAME}
+        DEF_PAIR(IN_PORT),
+        DEF_PAIR(TABLE),
+        DEF_PAIR(NORMAL),
+        DEF_PAIR(FLOOD),
+        DEF_PAIR(ALL),
+        DEF_PAIR(CONTROLLER),
+        DEF_PAIR(LOCAL),
+        DEF_PAIR(NONE),
+#undef DEF_PAIR
+    };
+    static const int n_pairs = ARRAY_SIZE(pairs);
+    size_t i;
+
+    for (i = 0; i < n_pairs; i++) {
+        if (!strcasecmp(name, pairs[i].name)) {
+            *port = pairs[i].value;
+            return true;
+        }
+    }
+    return false;
+}
+
+static void
+str_to_action(char *str, struct ofpbuf *b)
+{
+    char *act, *arg;
+    char *saveptr = NULL;
+    bool drop = false;
+    int n_actions;
+
+    for (act = strtok_r(str, ", \t\r\n", &saveptr), n_actions = 0; act;
+         act = strtok_r(NULL, ", \t\r\n", &saveptr), n_actions++) 
+    {
+        uint16_t port;
+
+        if (drop) {
+            ovs_fatal(0, "Drop actions must not be followed by other actions");
+        }
+
+        /* Arguments are separated by colons */
+        arg = strchr(act, ':');
+        if (arg) {
+            *arg = '\0';
+            arg++;
+        }
+
+        if (!strcasecmp(act, "mod_vlan_vid")) {
+            struct ofp_action_vlan_vid *va;
+            va = put_action(b, sizeof *va, OFPAT_SET_VLAN_VID);
+            va->vlan_vid = htons(str_to_u32(arg));
+        } else if (!strcasecmp(act, "mod_vlan_pcp")) {
+            struct ofp_action_vlan_pcp *va;
+            va = put_action(b, sizeof *va, OFPAT_SET_VLAN_PCP);
+            va->vlan_pcp = str_to_u32(arg);
+        } else if (!strcasecmp(act, "strip_vlan")) {
+            struct ofp_action_header *ah;
+            ah = put_action(b, sizeof *ah, OFPAT_STRIP_VLAN);
+            ah->type = htons(OFPAT_STRIP_VLAN);
+        } else if (!strcasecmp(act, "mod_dl_src")) {
+            put_dl_addr_action(b, OFPAT_SET_DL_SRC, arg);
+        } else if (!strcasecmp(act, "mod_dl_dst")) {
+            put_dl_addr_action(b, OFPAT_SET_DL_DST, arg);
+        } else if (!strcasecmp(act, "output")) {
+            put_output_action(b, str_to_u32(arg));
+        } else if (!strcasecmp(act, "drop")) {
+            /* A drop action in OpenFlow occurs by just not setting 
+             * an action. */
+            drop = true;
+            if (n_actions) {
+                ovs_fatal(0, "Drop actions must not be preceded by other "
+                          "actions");
+            }
+#ifdef SUPPORT_SNAT
+        } else if (!strcasecmp(act, "nat")) {
+            struct nx_action_snat *sa;
+
+            if (str_to_u32(arg) > OFPP_MAX) {
+                ovs_fatal(0, "Invalid nat port: %s\n", arg);
+            }
+
+            sa = put_action(b, sizeof *sa, OFPAT_VENDOR);
+            sa->vendor = htonl(NX_VENDOR_ID);
+            sa->subtype = htons(NXAST_SNAT);
+            sa->port = htons(str_to_u32(arg));
+#endif
+        } else if (!strcasecmp(act, "CONTROLLER")) {
+            struct ofp_action_output *oao;
+            oao = put_output_action(b, OFPP_CONTROLLER);
+
+            /* Unless a numeric argument is specified, we send the whole
+             * packet to the controller. */
+            if (arg && (strspn(act, "0123456789") == strlen(act))) {
+               oao->max_len = htons(str_to_u32(arg));
+            }
+        } else if (parse_port_name(act, &port)) {
+            put_output_action(b, port);
+        } else if (strspn(act, "0123456789") == strlen(act)) {
+            put_output_action(b, str_to_u32(act));
+        } else {
+            ovs_fatal(0, "Unknown action: %s", act);
+        }
+    }
+}
+
+struct protocol {
+    const char *name;
+    uint16_t dl_type;
+    uint8_t nw_proto;
+};
+
+static bool
+parse_protocol(const char *name, const struct protocol **p_out)
+{
+    static const struct protocol protocols[] = {
+        { "ip", ETH_TYPE_IP, 0 },
+        { "arp", ETH_TYPE_ARP, 0 },
+        { "icmp", ETH_TYPE_IP, IP_TYPE_ICMP },
+        { "tcp", ETH_TYPE_IP, IP_TYPE_TCP },
+        { "udp", ETH_TYPE_IP, IP_TYPE_UDP },
+    };
+    const struct protocol *p;
+
+    for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
+        if (!strcmp(p->name, name)) {
+            *p_out = p;
+            return true;
+        }
+    }
+    *p_out = NULL;
+    return false;
+}
+
+struct field {
+    const char *name;
+    uint32_t wildcard;
+    enum { F_U8, F_U16, F_MAC, F_IP } type;
+    size_t offset, shift;
+};
+
+static bool
+parse_field(const char *name, const struct field **f_out) 
+{
+#define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
+    static const struct field fields[] = { 
+        { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port), 0 },
+        { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan), 0 },
+        { "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src), 0 },
+        { "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst), 0 },
+        { "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type), 0 },
+        { "nw_src", OFPFW_NW_SRC_MASK, F_IP,
+          F_OFS(nw_src), OFPFW_NW_SRC_SHIFT },
+        { "nw_dst", OFPFW_NW_DST_MASK, F_IP,
+          F_OFS(nw_dst), OFPFW_NW_DST_SHIFT },
+        { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto), 0 },
+        { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src), 0 },
+        { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst), 0 },
+        { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type), 0 },
+        { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code), 0 }
+    };
+    const struct field *f;
+
+    for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
+        if (!strcmp(f->name, name)) {
+            *f_out = f;
+            return true;
+        }
+    }
+    *f_out = NULL;
+    return false;
+}
+
+static void
+str_to_flow(char *string, struct ofp_match *match, struct ofpbuf *actions,
+            uint8_t *table_idx, uint16_t *out_port, uint16_t *priority, 
+            uint16_t *idle_timeout, uint16_t *hard_timeout)
+{
+    char *save_ptr = NULL;
+    char *name;
+    uint32_t wildcards;
+
+    if (table_idx) {
+        *table_idx = 0xff;
+    }
+    if (out_port) {
+        *out_port = OFPP_NONE;
+    }
+    if (priority) {
+        *priority = OFP_DEFAULT_PRIORITY;
+    }
+    if (idle_timeout) {
+        *idle_timeout = DEFAULT_IDLE_TIMEOUT;
+    }
+    if (hard_timeout) {
+        *hard_timeout = OFP_FLOW_PERMANENT;
+    }
+    if (actions) {
+        char *act_str = strstr(string, "action");
+        if (!act_str) {
+            ovs_fatal(0, "must specify an action");
+        }
+        *(act_str-1) = '\0';
+
+        act_str = strchr(act_str, '=');
+        if (!act_str) {
+            ovs_fatal(0, "must specify an action");
+        }
+
+        act_str++;
+
+        str_to_action(act_str, actions);
+    }
+    memset(match, 0, sizeof *match);
+    wildcards = OFPFW_ALL;
+    for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+        const struct protocol *p;
+
+        if (parse_protocol(name, &p)) {
+            wildcards &= ~OFPFW_DL_TYPE;
+            match->dl_type = htons(p->dl_type);
+            if (p->nw_proto) {
+                wildcards &= ~OFPFW_NW_PROTO;
+                match->nw_proto = p->nw_proto;
+            }
+        } else {
+            const struct field *f;
+            char *value;
+
+            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+            if (!value) {
+                ovs_fatal(0, "field %s missing value", name);
+            }
+        
+            if (table_idx && !strcmp(name, "table")) {
+                *table_idx = atoi(value);
+            } else if (out_port && !strcmp(name, "out_port")) {
+                *out_port = atoi(value);
+            } else if (priority && !strcmp(name, "priority")) {
+                *priority = atoi(value);
+            } else if (idle_timeout && !strcmp(name, "idle_timeout")) {
+                *idle_timeout = atoi(value);
+            } else if (hard_timeout && !strcmp(name, "hard_timeout")) {
+                *hard_timeout = atoi(value);
+            } else if (parse_field(name, &f)) {
+                void *data = (char *) match + f->offset;
+                if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
+                    wildcards |= f->wildcard;
+                } else {
+                    wildcards &= ~f->wildcard;
+                    if (f->wildcard == OFPFW_IN_PORT
+                        && parse_port_name(value, (uint16_t *) data)) {
+                        /* Nothing to do. */
+                    } else if (f->type == F_U8) {
+                        *(uint8_t *) data = str_to_u32(value);
+                    } else if (f->type == F_U16) {
+                        *(uint16_t *) data = htons(str_to_u32(value));
+                    } else if (f->type == F_MAC) {
+                        str_to_mac(value, data);
+                    } else if (f->type == F_IP) {
+                        wildcards |= str_to_ip(value, data) << f->shift;
+                    } else {
+                        NOT_REACHED();
+                    }
+                }
+            } else {
+                ovs_fatal(0, "unknown keyword %s", name);
+            }
+        }
+    }
+    match->wildcards = htonl(wildcards);
+}
+
+static void
+do_dump_flows(const struct settings *s UNUSED, int argc, char *argv[])
+{
+    struct ofp_flow_stats_request *req;
+    uint16_t out_port;
+    struct ofpbuf *request;
+
+    req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
+    str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
+                &req->table_id, &out_port, NULL, NULL, NULL);
+    memset(&req->pad, 0, sizeof req->pad);
+    req->out_port = htons(out_port);
+
+    dump_stats_transaction(argv[1], request);
+}
+
+static void
+do_dump_aggregate(const struct settings *s UNUSED, int argc, char *argv[])
+{
+    struct ofp_aggregate_stats_request *req;
+    struct ofpbuf *request;
+    uint16_t out_port;
+
+    req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
+    str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
+                &req->table_id, &out_port, NULL, NULL, NULL);
+    memset(&req->pad, 0, sizeof req->pad);
+    req->out_port = htons(out_port);
+
+    dump_stats_transaction(argv[1], request);
+}
+
+#ifdef SUPPORT_SNAT
+static void
+do_add_snat(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    struct vconn *vconn;
+    struct ofpbuf *buffer;
+    struct nx_act_config *nac;
+    size_t size;
+
+    /* Parse and send. */
+    size = sizeof *nac + sizeof nac->snat[0];
+    nac = make_openflow(size, OFPT_VENDOR, &buffer);
+
+    nac->header.vendor = htonl(NX_VENDOR_ID);
+    nac->header.subtype = htonl(NXT_ACT_SET_CONFIG);
+
+    nac->type = htons(NXAST_SNAT);
+    nac->snat[0].command = NXSC_ADD;
+    nac->snat[0].port = htons(str_to_u32(argv[2]));
+    nac->snat[0].mac_timeout = htons(0);
+    str_to_ip(argv[3], &nac->snat[0].ip_addr_start);
+    str_to_ip(argv[3], &nac->snat[0].ip_addr_end);
+
+    open_vconn(argv[1], &vconn);
+    send_openflow_buffer(vconn, buffer);
+    vconn_close(vconn);
+}
+
+static void
+do_del_snat(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    struct vconn *vconn;
+    struct ofpbuf *buffer;
+    struct nx_act_config *nac;
+    size_t size;
+
+    /* Parse and send. */
+    size = sizeof *nac + sizeof nac->snat[0];
+    nac = make_openflow(size, OFPT_VENDOR, &buffer);
+
+    nac->header.vendor = htonl(NX_VENDOR_ID);
+    nac->header.subtype = htonl(NXT_ACT_SET_CONFIG);
+
+    nac->type = htons(NXAST_SNAT);
+    nac->snat[0].command = NXSC_DELETE;
+    nac->snat[0].port = htons(str_to_u32(argv[2]));
+    nac->snat[0].mac_timeout = htons(0);
+
+    open_vconn(argv[1], &vconn);
+    send_openflow_buffer(vconn, buffer);
+    vconn_close(vconn);
+}
+#endif /* SUPPORT_SNAT */
+
+static void
+do_add_flow(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    struct vconn *vconn;
+    struct ofpbuf *buffer;
+    struct ofp_flow_mod *ofm;
+    uint16_t priority, idle_timeout, hard_timeout;
+    struct ofp_match match;
+
+    /* Parse and send.  str_to_flow() will expand and reallocate the data in
+     * 'buffer', so we can't keep pointers to across the str_to_flow() call. */
+    make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+    str_to_flow(argv[2], &match, buffer,
+                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
+    ofm = buffer->data;
+    ofm->match = match;
+    ofm->command = htons(OFPFC_ADD);
+    ofm->idle_timeout = htons(idle_timeout);
+    ofm->hard_timeout = htons(hard_timeout);
+    ofm->buffer_id = htonl(UINT32_MAX);
+    ofm->priority = htons(priority);
+    ofm->reserved = htonl(0);
+
+    open_vconn(argv[1], &vconn);
+    send_openflow_buffer(vconn, buffer);
+    vconn_close(vconn);
+}
+
+static void
+do_add_flows(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    struct vconn *vconn;
+    FILE *file;
+    char line[1024];
+
+    file = fopen(argv[2], "r");
+    if (file == NULL) {
+        ovs_fatal(errno, "%s: open", argv[2]);
+    }
+
+    open_vconn(argv[1], &vconn);
+    while (fgets(line, sizeof line, file)) {
+        struct ofpbuf *buffer;
+        struct ofp_flow_mod *ofm;
+        uint16_t priority, idle_timeout, hard_timeout;
+        struct ofp_match match;
+
+        char *comment;
+
+        /* Delete comments. */
+        comment = strchr(line, '#');
+        if (comment) {
+            *comment = '\0';
+        }
+
+        /* Drop empty lines. */
+        if (line[strspn(line, " \t\n")] == '\0') {
+            continue;
+        }
+
+        /* Parse and send.  str_to_flow() will expand and reallocate the data
+         * in 'buffer', so we can't keep pointers to across the str_to_flow()
+         * call. */
+        ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+        str_to_flow(line, &match, buffer,
+                    NULL, NULL, &priority, &idle_timeout, &hard_timeout);
+        ofm = buffer->data;
+        ofm->match = match;
+        ofm->command = htons(OFPFC_ADD);
+        ofm->idle_timeout = htons(idle_timeout);
+        ofm->hard_timeout = htons(hard_timeout);
+        ofm->buffer_id = htonl(UINT32_MAX);
+        ofm->priority = htons(priority);
+        ofm->reserved = htonl(0);
+
+        send_openflow_buffer(vconn, buffer);
+    }
+    vconn_close(vconn);
+    fclose(file);
+}
+
+static void
+do_mod_flows(const struct settings *s, int argc UNUSED, char *argv[])
+{
+    uint16_t priority, idle_timeout, hard_timeout;
+    struct vconn *vconn;
+    struct ofpbuf *buffer;
+    struct ofp_flow_mod *ofm;
+
+    /* Parse and send. */
+    ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+    str_to_flow(argv[2], &ofm->match, buffer,
+                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
+    if (s->strict) {
+        ofm->command = htons(OFPFC_MODIFY_STRICT);
+    } else {
+        ofm->command = htons(OFPFC_MODIFY);
+    }
+    ofm->idle_timeout = htons(idle_timeout);
+    ofm->hard_timeout = htons(hard_timeout);
+    ofm->buffer_id = htonl(UINT32_MAX);
+    ofm->priority = htons(priority);
+    ofm->reserved = htonl(0);
+
+    open_vconn(argv[1], &vconn);
+    send_openflow_buffer(vconn, buffer);
+    vconn_close(vconn);
+}
+
+static void do_del_flows(const struct settings *s, int argc, char *argv[])
+{
+    struct vconn *vconn;
+    uint16_t priority;
+    uint16_t out_port;
+    struct ofpbuf *buffer;
+    struct ofp_flow_mod *ofm;
+
+    /* Parse and send. */
+    ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+    str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL, 
+                &out_port, &priority, NULL, NULL);
+    if (s->strict) {
+        ofm->command = htons(OFPFC_DELETE_STRICT);
+    } else {
+        ofm->command = htons(OFPFC_DELETE);
+    }
+    ofm->idle_timeout = htons(0);
+    ofm->hard_timeout = htons(0);
+    ofm->buffer_id = htonl(UINT32_MAX);
+    ofm->out_port = htons(out_port);
+    ofm->priority = htons(priority);
+    ofm->reserved = htonl(0);
+
+    open_vconn(argv[1], &vconn);
+    send_openflow_buffer(vconn, buffer);
+    vconn_close(vconn);
+}
+
+static void
+do_monitor(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    struct vconn *vconn;
+
+    open_vconn(argv[1], &vconn);
+    if (argc > 2) {
+        int miss_send_len = atoi(argv[2]);
+        int send_flow_exp = argc > 3 ? atoi(argv[3]) : 0;
+        struct ofp_switch_config *osc;
+        struct ofpbuf *buf;
+
+        osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf);
+        osc->flags = htons(send_flow_exp ? OFPC_SEND_FLOW_EXP : 0);
+        osc->miss_send_len = htons(miss_send_len);
+        send_openflow_buffer(vconn, buf);
+    }
+    for (;;) {
+        struct ofpbuf *b;
+        run(vconn_recv_block(vconn, &b), "vconn_recv");
+        ofp_print(stderr, b->data, b->size, 2);
+        ofpbuf_delete(b);
+    }
+}
+
+static void
+do_dump_ports(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    dump_trivial_stats_transaction(argv[1], OFPST_PORT);
+}
+
+static void
+do_probe(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    struct ofpbuf *request;
+    struct vconn *vconn;
+    struct ofpbuf *reply;
+
+    make_openflow(sizeof(struct ofp_header), OFPT_ECHO_REQUEST, &request);
+    open_vconn(argv[1], &vconn);
+    run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
+    if (reply->size != sizeof(struct ofp_header)) {
+        ovs_fatal(0, "reply does not match request");
+    }
+    ofpbuf_delete(reply);
+    vconn_close(vconn);
+}
+
+static void
+do_mod_port(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    struct ofpbuf *request, *reply;
+    struct ofp_switch_features *osf;
+    struct ofp_port_mod *opm;
+    struct vconn *vconn;
+    char *endptr;
+    int n_ports;
+    int port_idx;
+    int port_no;
+    
+
+    /* Check if the argument is a port index.  Otherwise, treat it as
+     * the port name. */
+    port_no = strtol(argv[2], &endptr, 10);
+    if (port_no == 0 && endptr == argv[2]) {
+        port_no = -1;
+    }
+
+    /* Send a "Features Request" to get the information we need in order 
+     * to modify the port. */
+    make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request);
+    open_vconn(argv[1], &vconn);
+    run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
+
+    osf = reply->data;
+    n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports;
+
+    for (port_idx = 0; port_idx < n_ports; port_idx++) {
+        if (port_no != -1) {
+            /* Check argument as a port index */
+            if (osf->ports[port_idx].port_no == htons(port_no)) {
+                break;
+            }
+        } else {
+            /* Check argument as an interface name */
+            if (!strncmp((char *)osf->ports[port_idx].name, argv[2], 
+                        sizeof osf->ports[0].name)) {
+                break;
+            }
+
+        }
+    }
+    if (port_idx == n_ports) {
+        ovs_fatal(0, "couldn't find monitored port: %s", argv[2]);
+    }
+
+    opm = make_openflow(sizeof(struct ofp_port_mod), OFPT_PORT_MOD, &request);
+    opm->port_no = osf->ports[port_idx].port_no;
+    memcpy(opm->hw_addr, osf->ports[port_idx].hw_addr, sizeof opm->hw_addr);
+    opm->config = htonl(0);
+    opm->mask = htonl(0);
+    opm->advertise = htonl(0);
+
+    printf("modifying port: %s\n", osf->ports[port_idx].name);
+
+    if (!strncasecmp(argv[3], MOD_PORT_CMD_UP, sizeof MOD_PORT_CMD_UP)) {
+        opm->mask |= htonl(OFPPC_PORT_DOWN);
+    } else if (!strncasecmp(argv[3], MOD_PORT_CMD_DOWN, 
+                sizeof MOD_PORT_CMD_DOWN)) {
+        opm->mask |= htonl(OFPPC_PORT_DOWN);
+        opm->config |= htonl(OFPPC_PORT_DOWN);
+    } else if (!strncasecmp(argv[3], MOD_PORT_CMD_FLOOD, 
+                sizeof MOD_PORT_CMD_FLOOD)) {
+        opm->mask |= htonl(OFPPC_NO_FLOOD);
+    } else if (!strncasecmp(argv[3], MOD_PORT_CMD_NOFLOOD, 
+                sizeof MOD_PORT_CMD_NOFLOOD)) {
+        opm->mask |= htonl(OFPPC_NO_FLOOD);
+        opm->config |= htonl(OFPPC_NO_FLOOD);
+    } else {
+        ovs_fatal(0, "unknown mod-port command '%s'", argv[3]);
+    }
+
+    send_openflow_buffer(vconn, request);
+
+    ofpbuf_delete(reply);
+    vconn_close(vconn);
+}
+
+static void
+do_ping(const struct settings *s UNUSED, int argc, char *argv[])
+{
+    size_t max_payload = 65535 - sizeof(struct ofp_header);
+    unsigned int payload;
+    struct vconn *vconn;
+    int i;
+
+    payload = argc > 2 ? atoi(argv[2]) : 64;
+    if (payload > max_payload) {
+        ovs_fatal(0, "payload must be between 0 and %zu bytes", max_payload);
+    }
+
+    open_vconn(argv[1], &vconn);
+    for (i = 0; i < 10; i++) {
+        struct timeval start, end;
+        struct ofpbuf *request, *reply;
+        struct ofp_header *rq_hdr, *rpy_hdr;
+
+        rq_hdr = make_openflow(sizeof(struct ofp_header) + payload,
+                               OFPT_ECHO_REQUEST, &request);
+        random_bytes(rq_hdr + 1, payload);
+
+        gettimeofday(&start, NULL);
+        run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact");
+        gettimeofday(&end, NULL);
+
+        rpy_hdr = reply->data;
+        if (reply->size != request->size
+            || memcmp(rpy_hdr + 1, rq_hdr + 1, payload)
+            || rpy_hdr->xid != rq_hdr->xid
+            || rpy_hdr->type != OFPT_ECHO_REPLY) {
+            printf("Reply does not match request.  Request:\n");
+            ofp_print(stdout, request, request->size, 2);
+            printf("Reply:\n");
+            ofp_print(stdout, reply, reply->size, 2);
+        }
+        printf("%d bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
+               reply->size - sizeof *rpy_hdr, argv[1], rpy_hdr->xid,
+                   (1000*(double)(end.tv_sec - start.tv_sec))
+                   + (.001*(end.tv_usec - start.tv_usec)));
+        ofpbuf_delete(request);
+        ofpbuf_delete(reply);
+    }
+    vconn_close(vconn);
+}
+
+static void
+do_benchmark(const struct settings *s UNUSED, int argc UNUSED, char *argv[])
+{
+    size_t max_payload = 65535 - sizeof(struct ofp_header);
+    struct timeval start, end;
+    unsigned int payload_size, message_size;
+    struct vconn *vconn;
+    double duration;
+    int count;
+    int i;
+
+    payload_size = atoi(argv[2]);
+    if (payload_size > max_payload) {
+        ovs_fatal(0, "payload must be between 0 and %zu bytes", max_payload);
+    }
+    message_size = sizeof(struct ofp_header) + payload_size;
+
+    count = atoi(argv[3]);
+
+    printf("Sending %d packets * %u bytes (with header) = %u bytes total\n",
+           count, message_size, count * message_size);
+
+    open_vconn(argv[1], &vconn);
+    gettimeofday(&start, NULL);
+    for (i = 0; i < count; i++) {
+        struct ofpbuf *request, *reply;
+        struct ofp_header *rq_hdr;
+
+        rq_hdr = make_openflow(message_size, OFPT_ECHO_REQUEST, &request);
+        memset(rq_hdr + 1, 0, payload_size);
+        run(vconn_transact(vconn, request, &reply), "transact");
+        ofpbuf_delete(reply);
+    }
+    gettimeofday(&end, NULL);
+    vconn_close(vconn);
+
+    duration = ((1000*(double)(end.tv_sec - start.tv_sec))
+                + (.001*(end.tv_usec - start.tv_usec)));
+    printf("Finished in %.1f ms (%.0f packets/s) (%.0f bytes/s)\n",
+           duration, count / (duration / 1000.0),
+           count * message_size / (duration / 1000.0));
+}
+
+static void
+do_execute(const struct settings *s UNUSED, int argc, char *argv[])
+{
+    struct vconn *vconn;
+    struct ofpbuf *request;
+    struct nicira_header *nicira;
+    struct nx_command_reply *ncr;
+    uint32_t xid;
+    int i;
+
+    nicira = make_openflow(sizeof *nicira, OFPT_VENDOR, &request);
+    xid = nicira->header.xid;
+    nicira->vendor = htonl(NX_VENDOR_ID);
+    nicira->subtype = htonl(NXT_COMMAND_REQUEST);
+    ofpbuf_put(request, argv[2], strlen(argv[2]));
+    for (i = 3; i < argc; i++) {
+        ofpbuf_put_zeros(request, 1);
+        ofpbuf_put(request, argv[i], strlen(argv[i]));
+    }
+    update_openflow_length(request);
+
+    open_vconn(argv[1], &vconn);
+    run(vconn_send_block(vconn, request), "send");
+
+    for (;;) {
+        struct ofpbuf *reply;
+        uint32_t status;
+
+        run(vconn_recv_xid(vconn, xid, &reply), "recv_xid");
+        if (reply->size < sizeof *ncr) {
+            ovs_fatal(0, "reply is too short (%zu bytes < %zu bytes)",
+                      reply->size, sizeof *ncr);
+        }
+        ncr = reply->data;
+        if (ncr->nxh.header.type != OFPT_VENDOR
+            || ncr->nxh.vendor != htonl(NX_VENDOR_ID)
+            || ncr->nxh.subtype != htonl(NXT_COMMAND_REPLY)) {
+            ovs_fatal(0, "reply is invalid");
+        }
+
+        status = ntohl(ncr->status);
+        if (status & NXT_STATUS_STARTED) {
+            /* Wait for a second reply. */
+            continue;
+        } else if (status & NXT_STATUS_EXITED) {
+            fprintf(stderr, "process terminated normally with exit code %d",
+                    status & NXT_STATUS_EXITSTATUS);
+        } else if (status & NXT_STATUS_SIGNALED) {
+            fprintf(stderr, "process terminated by signal %d",
+                    status & NXT_STATUS_TERMSIG);
+        } else if (status & NXT_STATUS_ERROR) {
+            fprintf(stderr, "error executing command");
+        } else {
+            fprintf(stderr, "process terminated for unknown reason");
+        }
+        if (status & NXT_STATUS_COREDUMP) {
+            fprintf(stderr, " (core dumped)");
+        }
+        putc('\n', stderr);
+
+        fwrite(ncr + 1, reply->size - sizeof *ncr, 1, stdout);
+        break;
+    }
+}
+
+static void
+do_help(const struct settings *s UNUSED, int argc UNUSED, char *argv[] UNUSED)
+{
+    usage();
+}
+
+static struct command all_commands[] = {
+    { "show", 1, 1, do_show },
+    { "status", 1, 2, do_status },
+    { "monitor", 1, 3, do_monitor },
+    { "dump-desc", 1, 1, do_dump_desc },
+    { "dump-tables", 1, 1, do_dump_tables },
+    { "dump-flows", 1, 2, do_dump_flows },
+    { "dump-aggregate", 1, 2, do_dump_aggregate },
+#ifdef SUPPORT_SNAT
+    { "add-snat", 3, 3, do_add_snat },
+    { "del-snat", 2, 2, do_del_snat },
+#endif
+    { "add-flow", 2, 2, do_add_flow },
+    { "add-flows", 2, 2, do_add_flows },
+    { "mod-flows", 2, 2, do_mod_flows },
+    { "del-flows", 1, 2, do_del_flows },
+    { "dump-ports", 1, 1, do_dump_ports },
+    { "mod-port", 3, 3, do_mod_port },
+    { "probe", 1, 1, do_probe },
+    { "ping", 1, 2, do_ping },
+    { "benchmark", 3, 3, do_benchmark },
+    { "execute", 2, INT_MAX, do_execute },
+    { "help", 0, INT_MAX, do_help },
+    { NULL, 0, 0, NULL },
+};
index aa58d3fc3ae89337e85d9a6df842f82019295693..fb3e4e42f2eb90922f09bf138b92fdcf382f67ca 100644 (file)
@@ -319,6 +319,5 @@ Prints a help usage message and exits.
 .SH "SEE ALSO"
 
 .BR controller (8),
-.BR dpctl (8),
 .BR ovs\-pki\-cgi (8),
 .BR secchan (8)
index fa2463764cc8423c346553c7faa8cde032661d04..6fb9ccde289abfa5d28ed05000a83699c76cba57 100644 (file)
@@ -1619,7 +1619,7 @@ process_flow(struct bridge *br, const flow_t *flow,
              * - We deleted an interface but there are still a few packets
              *   queued up from it.
              *
-             * - Someone externally added an interface (e.g. with "dpctl
+             * - Someone externally added an interface (e.g. with "ovs-dpctl
              *   addif") that we don't know about.
              *
              * - Packet arrived on the local port but the local port is not
index 737ef364cf2f944dd6db1680cb801246c785c744..24357004ad744d229c0ffabd650c250e377a296e 100644 (file)
@@ -50,10 +50,10 @@ A single \fBvswitchd\fR can manage any number of virtual switches, up
 to the maximum number of supported OpenVSwitch datapaths.
 .PP
 \fBvswitchd\fR does all the necessary management of OpenVSwitch datapaths
-itself.  Thus, external tools, such \fBdpctl\fR(8), are not needed for
+itself.  Thus, external tools, such \fBovs\-dpctl\fR(8), are not needed for
 managing datapaths in conjunction with \fBvswitchd\fR, and their use
 to modify datapaths when \fBvswitchd\fR is running can interfere with
-its operation.  (\fBdpctl\fR may still be useful for diagnostics.)
+its operation.  (\fBovs\-dpctl\fR may still be useful for diagnostics.)
 .PP
 An OpenVSwitch datapath kernel module must be loaded for \fBvswitchd\fR
 to be useful.  Please refer to the \fBINSTALL\fR file included in the
index 53bdcecc08fed07bd675873ac7348d5dbdcf176e..7b61ece86085f0c380c9f75560f0b636f6ed953a 100644 (file)
@@ -529,7 +529,7 @@ using the \fBmgmt.burst-limit\fR key.  This option takes effect only
 when a rate-limit is specified.
 .ST "Remote Command Execution Settings"
 These settings configure the commands that remote OpenFlow connections
-are allowed to invoke using (e.g.) \fBdpctl execute\fR.  To be
+are allowed to invoke using (e.g.) \fBovs\-ofctl execute\fR.  To be
 permitted, a command name must be whitelisted and must not be
 blacklisted.  When the whitelist and blacklist permit a command name,
 \fBvswitchd\fR looks for a program with the same name as the command
@@ -597,7 +597,7 @@ require the controller to send the CA certificate, but
 By default, each bridge \fIname\fR listens for OpenFlow management
 connections on a Unix domain socket named
 \fB@RUNDIR@/\fIname\fB.mgmt\fR.  This socket can be used to perform
-local OpenFlow monitoring and administration, e.g., \fBdpctl dump-flows
+local OpenFlow monitoring and administration, e.g., \fBovs\-ofctl dump-flows
 unix:@RUNDIR@/\fIname\fB.mgmt\fR to display the flows currently set up
 in bridge \fIname\fR.
 .PP
@@ -622,7 +622,7 @@ To entirely disable listening for management connections, set
 By default, each bridge \fIname\fR listens for OpenFlow controller
 connection snooping connections on a Unix domain socket named
 \fB@RUNDIR@/\fIname\fB.snoop\fR.  A client that connects to this
-socket, e.g., \fBdpctl monitor unix:@RUNDIR@/\fIname\fB.snoop\fR, will
+socket, e.g., \fBovs\-ofctl monitor unix:@RUNDIR@/\fIname\fB.snoop\fR, will
 receive a copy of every OpenFlow message sent by the switch to the
 controller, or vice versa, on the primary OpenFlow controller
 connection.
index 8be18cb742fadf7fa1a4e6ad26c3892eb391bc9c..932ba7cbd953d249f1bb77bd84b397df6617ba61 100755 (executable)
@@ -62,8 +62,9 @@ BRCOMPATD_VALGRIND_OPT="${BRCOMPATD_VALGRIND_OPT:-}"
 # Full paths to executables & modules
 vswitchd="$VSWITCH_BASE/sbin/vswitchd"
 brcompatd="$VSWITCH_BASE/sbin/brcompatd"
-dpctl="$VSWITCH_BASE/bin/dpctl"
+dpctl="$VSWITCH_BASE/bin/ovs-dpctl"
 appctl="$VSWITCH_BASE/bin/ovs-appctl"
+ofctl="$VSWITCH_BASE/bin/ovs-ofctl"
 
 
 if [ "$ENABLE_FAKE_PROC_NET" == "y" ]; then
@@ -119,8 +120,8 @@ function allow_xen_mgmt_traffic {
     local mgmt_hwaddr=$(xen_pifdev_hwaddr "$mgmt_pifdev")
     test -n "$mgmt_hwaddr" || return
     action "Inserting dl_addr $mgmt_hwaddr flows for mgmt intf" true
-    "$dpctl" add-flow "$mgmt_intf" dl_src="$mgmt_hwaddr",idle_timeout=0,priority=0,action=normal
-    "$dpctl" add-flow "$mgmt_intf" dl_dst="$mgmt_hwaddr",idle_timeout=0,priority=0,action=normal
+    "$ofctl" add-flow "$mgmt_intf" dl_src="$mgmt_hwaddr",idle_timeout=0,priority=0,action=normal
+    "$ofctl" add-flow "$mgmt_intf" dl_dst="$mgmt_hwaddr",idle_timeout=0,priority=0,action=normal
 }
 
 function ifup_dp_intf {
index 1b691791f33f2c3f89d8962ada9118d0f4132555..7905491e982c427a0df79bc46d7c47f48063946e 100644 (file)
@@ -17,7 +17,7 @@ function watchconf {
 }
 
 function watchdp {
-       watch dpctl dp-show "$@"
+       watch ovs-dpctl dp-show "$@"
 }
 
 function watchdpflows {
@@ -27,18 +27,18 @@ function watchdpflows {
        if [ $# -gt 0 ]; then
                grep="| grep $@"
        fi
-       watch "dpctl dp-dump-flows $dp $grep"
+       watch "ovs-dpctl dp-dump-flows $dp $grep"
 }
 
 function watchflows {
        local grep=""
        local dp=$1
        shift
-       bridge=$(dpctl dp-show $dp | grep 'port 0:' | cut -d' ' -f 3)
+       bridge=$(ovs-dpctl dp-show $dp | grep 'port 0:' | cut -d' ' -f 3)
        if [ $# -gt 0 ]; then
                grep="| grep $@"
        fi
-       watch "dpctl dump-flows unix:/var/run/$bridge.mgmt $grep"
+       watch "ovs-ofctl dump-flows unix:/var/run/$bridge.mgmt $grep"
 }
 
 function monitorlogs {
index 2199c95ff052fb57dabef269c7c0e5ec57993f42..5640c85b9780650659cec8930eb97ea27c6bf0f4 100644 (file)
@@ -259,12 +259,14 @@ fi
 /root/vswitch/scripts/XSFeatureNiciraVSwitch.pyo
 /root/vswitch/sbin/brcompatd
 /root/vswitch/sbin/vswitchd
-/root/vswitch/bin/dpctl
 /root/vswitch/bin/ovs-appctl
 /root/vswitch/bin/ovs-cfg-mod
+/root/vswitch/bin/ovs-dpctl
+/root/vswitch/bin/ovs-ofctl
 /root/vswitch/share/man/man5/vswitchd.conf.5
 /root/vswitch/share/man/man8/brcompatd.8
 /root/vswitch/share/man/man8/ovs-appctl.8
 /root/vswitch/share/man/man8/ovs-cfg-mod.8
-/root/vswitch/share/man/man8/dpctl.8
+/root/vswitch/share/man/man8/ovs-dpctl.8
+/root/vswitch/share/man/man8/ovs-ofctl.8
 /root/vswitch/share/man/man8/vswitchd.8