From: Ben Pfaff Date: Fri, 15 May 2009 22:48:58 +0000 (-0700) Subject: Break dpctl into two programs: ovs-ofctl and ovs-dpctl. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=042bb01062a0c302e654bb925f60f522cd2e158b;p=openvswitch Break dpctl into two programs: ovs-ofctl and ovs-dpctl. 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. --- diff --git a/INSTALL b/INSTALL index dbab025f..5aa7073e 100644 --- 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 e4a7c714..737029a1 100644 --- 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. diff --git a/debian/openvswitch-switch.init b/debian/openvswitch-switch.init index 45a87d25..3f96c914 100755 --- a/debian/openvswitch-switch.init +++ b/debian/openvswitch-switch.init @@ -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) diff --git a/debian/openvswitch-switch.install b/debian/openvswitch-switch.install index a43c9c60..9fddacf0 100644 --- a/debian/openvswitch-switch.install +++ b/debian/openvswitch-switch.install @@ -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 diff --git a/debian/openvswitch-switch.manpages b/debian/openvswitch-switch.manpages index 80bfd9fd..f789eba9 100644 --- a/debian/openvswitch-switch.manpages +++ b/debian/openvswitch-switch.manpages @@ -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 diff --git a/debian/openvswitch-switch.template b/debian/openvswitch-switch.template index 704a3bbd..739475bc 100644 --- a/debian/openvswitch-switch.template +++ b/debian/openvswitch-switch.template @@ -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. diff --git a/debian/ovs-switch-setup.8 b/debian/ovs-switch-setup.8 index f369587c..e34656d7 100644 --- a/debian/ovs-switch-setup.8 +++ b/debian/ovs-switch-setup.8 @@ -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) diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def index e9221e0d..4ef458ab 100644 --- a/lib/vlog-modules.def +++ b/lib/vlog-modules.def @@ -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) diff --git a/secchan/secchan.8.in b/secchan/secchan.8.in index e456c530..8a842947 100644 --- a/secchan/secchan.8.in +++ b/secchan/secchan.8.in @@ -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) diff --git a/utilities/.gitignore b/utilities/.gitignore index 7cfc5c49..32a7f2eb 100644 --- a/utilities/.gitignore +++ b/utilities/.gitignore @@ -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 diff --git a/utilities/automake.mk b/utilities/automake.mk index 35c303c3..97b827ac 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -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 index c88d3d3b..00000000 --- a/utilities/dpctl.8.in +++ /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 index 15ea4883..00000000 --- a/utilities/dpctl.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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); - } -} - -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); -} - -/* 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 }, -}; diff --git a/utilities/ovs-appctl.8.in b/utilities/ovs-appctl.8.in index 5204a800..f4538179 100644 --- a/utilities/ovs-appctl.8.in +++ b/utilities/ovs-appctl.8.in @@ -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) diff --git a/utilities/ovs-controller.8.in b/utilities/ovs-controller.8.in index 0f8e775f..c28efac3 100644 --- a/utilities/ovs-controller.8.in +++ b/utilities/ovs-controller.8.in @@ -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) diff --git a/utilities/ovs-discover.8.in b/utilities/ovs-discover.8.in index 12a950e8..423fa42c 100644 --- a/utilities/ovs-discover.8.in +++ b/utilities/ovs-discover.8.in @@ -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 index 00000000..61f2c694 --- /dev/null +++ b/utilities/ovs-dpctl.8.in @@ -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 index 00000000..a8c7b1be --- /dev/null +++ b/utilities/ovs-dpctl.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + } +} + +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 }, +}; diff --git a/utilities/ovs-monitor b/utilities/ovs-monitor index 07a56c33..4e098612 100755 --- a/utilities/ovs-monitor +++ b/utilities/ovs-monitor @@ -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 index 00000000..1243f04b --- /dev/null +++ b/utilities/ovs-ofctl.8.in @@ -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 index 00000000..e5acffca --- /dev/null +++ b/utilities/ovs-ofctl.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + } +} + +/* 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 }, +}; diff --git a/utilities/ovs-pki.8.in b/utilities/ovs-pki.8.in index aa58d3fc..fb3e4e42 100644 --- a/utilities/ovs-pki.8.in +++ b/utilities/ovs-pki.8.in @@ -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) diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index fa246376..6fb9ccde 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -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 diff --git a/vswitchd/vswitchd.8.in b/vswitchd/vswitchd.8.in index 737ef364..24357004 100644 --- a/vswitchd/vswitchd.8.in +++ b/vswitchd/vswitchd.8.in @@ -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 diff --git a/vswitchd/vswitchd.conf.5.in b/vswitchd/vswitchd.conf.5.in index 53bdcecc..7b61ece8 100644 --- a/vswitchd/vswitchd.conf.5.in +++ b/vswitchd/vswitchd.conf.5.in @@ -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. diff --git a/xenserver/etc_init.d_vswitch b/xenserver/etc_init.d_vswitch index 8be18cb7..932ba7cb 100755 --- a/xenserver/etc_init.d_vswitch +++ b/xenserver/etc_init.d_vswitch @@ -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 { diff --git a/xenserver/etc_profile.d_vswitch.sh b/xenserver/etc_profile.d_vswitch.sh index 1b691791..7905491e 100644 --- a/xenserver/etc_profile.d_vswitch.sh +++ b/xenserver/etc_profile.d_vswitch.sh @@ -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 { diff --git a/xenserver/vswitch-xen.spec b/xenserver/vswitch-xen.spec index 2199c95f..5640c85b 100644 --- a/xenserver/vswitch-xen.spec +++ b/xenserver/vswitch-xen.spec @@ -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