Merge master branch into xs5.7.
authorBen Pfaff <blp@nicira.com>
Wed, 18 Nov 2009 22:26:55 +0000 (14:26 -0800)
committerBen Pfaff <blp@nicira.com>
Wed, 18 Nov 2009 22:26:55 +0000 (14:26 -0800)
Conflicts:
xenserver/README
xenserver/automake.mk
xenserver/etc_xensource_scripts_vif
xenserver/usr_share_vswitch_scripts_dump-vif-details
xenserver/vswitch-xen.spec

73 files changed:
ChangeLog
INSTALL.Linux
INSTALL.userspace [new file with mode: 0644]
Makefile.am
README
acinclude.m4
configure.ac
datapath/actions.c
datapath/actions.h
datapath/datapath.c
datapath/datapath.h
datapath/dp_dev.c
datapath/flow.c
datapath/flow.h
datapath/linux-2.6/Makefile.main.in
datapath/linux-2.6/Modules.mk
datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
datapath/linux-2.6/compat-2.6/include/net/checksum.h
debian/openvswitch-switch.logrotate
extras/ezio/ovs-switchui.c
include/openflow/openflow.h
include/openvswitch/datapath-protocol.h
lib/backtrace.c
lib/cfg.c
lib/daemon.c
lib/dpif-linux.c
lib/dpif-netdev.c
lib/fault.c
lib/flow.c
lib/leak-checker.c
lib/mac-learning.c
lib/mac-learning.h
lib/odp-util.c
lib/ofp-print.c
lib/pcap.c
lib/poll-loop.c
lib/timeval.c
lib/timeval.h
lib/unixctl.c
lib/vconn.c
lib/vlog-unixctl.man
lib/vlog.man
ofproto/in-band.c
ofproto/netflow.c
ofproto/netflow.h
ofproto/ofproto.c
ofproto/ofproto.h
tests/ovs-vsctl.at
utilities/ovs-appctl.8.in
utilities/ovs-appctl.c
utilities/ovs-controller.c
utilities/ovs-dpctl.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-openflowd.8.in
utilities/ovs-openflowd.c
utilities/ovs-vsctl.8.in
utilities/ovs-vsctl.in
vswitchd/INTERNALS
vswitchd/bridge.c
vswitchd/mgmt.c
vswitchd/ovs-brcompatd.8.in
vswitchd/ovs-brcompatd.c
vswitchd/ovs-vswitchd.8.in
vswitchd/ovs-vswitchd.conf.5.in
xenserver/LICENSE [new file with mode: 0644]
xenserver/README
xenserver/automake.mk
xenserver/etc_init.d_vswitch
xenserver/etc_xensource_scripts_vif
xenserver/opt_xensource_libexec_interface-reconfigure
xenserver/usr_share_vswitch_scripts_refresh-xs-network-uuids [new file with mode: 0755]
xenserver/vswitch-xen.spec

index f2f56a3..eff97aa 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+v0.90.6 - 6 Oct 2009
+--------------------
+    - Bug fixes
+
 v0.90.5 - 21 Sep 2009
 ---------------------
     - Generalize in-band control to more diverse network setups
index 9767f99..7090ce6 100644 (file)
@@ -27,8 +27,11 @@ you will need the following software:
       connections from an Open vSwitch to an OpenFlow controller.  To
       enable, configure with --enable-ssl=yes.
 
-To compile the kernel module (which is required for operation), you
-must also install the following:
+To compile the kernel module, you must also install the following.  If
+you cannot build or install the kernel module, you may use the
+userspace-only implementation, at a cost in performance.  The
+userspace implementation may also lack some features.  Refer to
+INSTALL.userspace for more information.
 
     - A supported Linux kernel version.  Please refer to README for a
       list of supported versions.
@@ -58,7 +61,7 @@ If you are working from a Git tree or snapshot (instead of from a
 distribution tarball), or if you modify the Open vSwitch build system,
 you will also need the following software:
 
-    - Autoconf version 2.60 or later.
+    - Autoconf version 2.63 or later.
 
     - Automake version 1.10 or later.
 
@@ -92,7 +95,12 @@ Building and Installing Open vSwitch for Linux
 Once you have installed all the prerequisites listed above in the Base
 Prerequisites section, follow the procedure below to build.
 
-1. In the top source directory, configure the package by running the
+1. If you pulled the sources directly from an Open vSwitch Git tree, 
+   run boot.sh in the top source directory:
+
+      % ./boot.sh
+
+2. In the top source directory, configure the package by running the
    configure script.  You can usually invoke configure without any
    arguments:
 
@@ -128,16 +136,16 @@ Prerequisites section, follow the procedure below to build.
    additional environment variables.  For a full list, invoke
    configure with the --help option.
 
-2. Run make in the top source directory: 
+3. Run make in the top source directory: 
 
       % make
 
-3. Become root by running "su" or another program.
+4. Become root by running "su" or another program.
 
-4. Run "make install" to install the executables and manpages into the
+5. Run "make install" to install the executables and manpages into the
    running system, by default under /usr/local.
 
-5. If you built kernel modules, you may load them with "insmod", e.g.:
+6. If you built kernel modules, you may load them with "insmod", e.g.:
 
       % insmod datapath/linux-2.6/openvswitch_mod.ko
 
@@ -156,14 +164,12 @@ respectively.
 
 At runtime, you may make ovs-vswitchd reload its configuration file
 and update its configuration accordingly by sending it a SIGHUP
-signal.  The ovs-appctl utility can also be used to do this with a
-command such as:
+signal.  The ovs-appctl utility can also be used to do this:
 
-    % ovs-appctl -t <pid> -e vswitchd/reload
+    % ovs-appctl vswitchd/reload
 
-where <pid> is ovs-vswitchd's process ID.  In the latter case,
-ovs-appctl will not exit until the reload and reconfiguration is
-complete.
+In the latter case, ovs-appctl will wait for ovs-vswitchd to finish
+reloading before it exits.
 
 Bug Reporting
 -------------
diff --git a/INSTALL.userspace b/INSTALL.userspace
new file mode 100644 (file)
index 0000000..a0c6a26
--- /dev/null
@@ -0,0 +1,51 @@
+              Using Open vSwitch without kernel support
+              =========================================
+
+Open vSwitch can operate, at a cost in performance, entirely in
+userspace, without assistance from a kernel module.  This file
+explains how to install Open vSwitch in such a mode.
+
+The userspace-only mode of Open vSwitch is considered experimental.
+It has not been thoroughly tested.
+
+This version of Open vSwitch should be built manually with "configure"
+and "make".  Debian packaging for Open vSwitch is also included, but
+it has not been recently tested, and so Debian packages are not a
+recommended way to use this version of Open vSwitch.
+
+Building and Installing
+-----------------------
+
+The requirements and procedure for building, installing, and
+configuring Open vSwitch are the same as those given in INSTALL.Linux.
+You may omit configuring, building, and installing the kernel module,
+and the related requirements.
+
+On Linux, the userspace switch additionally requires the kernel
+TUN/TAP driver to be available, either built into the kernel or loaded
+as a module.  If you are not sure, check for a directory named
+/sys/class/misc/tun.  If it does not exist, then attempt to load the
+module with "modprobe tun".
+
+The tun device must also exist as /dev/net/tun.  If it does not exist,
+then create /dev/net (if necessary) with "mkdir /dev/net", then create
+/dev/net/tun with "mknod /dev/net/tun c 10 200".
+
+Using the Userspace Datapath
+----------------------------
+
+To use ovs-vswitchd in userspace mode, give the bridge a name that
+begins with "netdev:" in the configuration file.  For example:
+
+    bridge.netdev:br0.port=eth0
+    bridge.netdev:br0.port=eth1
+    bridge.netdev:br0.port=eth2
+
+ovs-vswitchd will create a TAP device as the bridge's local interface,
+named the same as the bridge minus the "netdev:" prefix, as well as
+for each configured internal interface.
+
+Bug Reporting
+-------------
+
+Please report problems to bugs@openvswitch.org.
index 60fd21e..add0e72 100644 (file)
@@ -28,6 +28,7 @@ CLEANFILES =
 DISTCLEANFILES =
 EXTRA_DIST = INSTALL.bridge \
        INSTALL.Linux \
+       INSTALL.userspace \
        INSTALL.OpenFlow \
        INSTALL.SSL \
        INSTALL.XenServer 
diff --git a/README b/README
index 2bdbfdc..7871c76 100644 (file)
--- a/README
+++ b/README
@@ -28,9 +28,15 @@ vSwitch supports the following features:
     * Support for OpenFlow
     * Compatibility layer for the Linux bridging code
 
-Open vSwitch supports Linux 2.6.15 and up, with testing focused on
-2.6.18 with Centos and Xen patches and version 2.6.26 from kernel.org.
-Open vSwitch also has special support for Citrix XenServer hosts.
+The included Linux kernel module supports Linux 2.6.15 and up, with
+testing focused on 2.6.18 with Centos and Xen patches and version
+2.6.26 from kernel.org.  Open vSwitch also has special support for
+Citrix XenServer hosts.
+
+Open vSwitch can also operate, at a cost in performance, entirely in
+userspace, without assistance from a kernel module.  This userspace
+implementation should be easier to port than the kernel-based switch.
+It is considered experimental.
 
 What's here?
 ------------
@@ -85,6 +91,9 @@ read INSTALL.bridge.
 To build RPMs for installing Open vSwitch on a Citrix XenServer host
 or resource pool, read INSTALL.XenServer.
 
+To install Open vSwitch without using a kernel module, read
+INSTALL.userspace.
+
 To learn set up SSL support for Open vSwitch, read INSTALL.SSL.
 
 Each Open vSwitch userspace program is accompanied by a manpage.  Many
index 2f38997..6115669 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-dnl OVS_CHECK_LINUX(OPTION, VERSION, VARIABLE, CONDITIONAL)
+dnl OVS_CHECK_LINUX26
 dnl
 dnl Configure linux kernel source tree 
-AC_DEFUN([OVS_CHECK_LINUX], [
-  AC_ARG_WITH([$1],
-              [AC_HELP_STRING([--with-$1=/path/to/linux-$2],
-                              [Specify the linux $2 kernel sources])],
-              [path="$withval"], [path=])dnl
-  if test -n "$path"; then
-    path=`eval echo "$path"`
+AC_DEFUN([OVS_CHECK_LINUX26], [
+  AC_ARG_WITH([l26],
+              [AC_HELP_STRING([--with-l26=/path/to/linux-2.6],
+                              [Specify the linux 2.6 kernel sources])],
+              [KBUILD26="$withval"], [KBUILD26=])dnl
+  if test -n "$KBUILD26"; then
+    KBUILD26=`eval echo "$KBUILD26"`
 
-    AC_MSG_CHECKING([for $path directory])
-    if test -d "$path"; then
-       AC_MSG_RESULT([yes])
-       $3=$path
-       AC_SUBST($3)
+    # The build directory is what the user provided.
+    # Make sure that it exists.
+    AC_MSG_CHECKING([for Linux 2.6 build directory])
+    if test -d "$KBUILD26"; then
+       AC_MSG_RESULT([$KBUILD26])
+       AC_SUBST(KBUILD26)
     else
        AC_MSG_RESULT([no])
-       AC_ERROR([source dir $path doesn't exist])
+       AC_ERROR([source dir $KBUILD26 doesn't exist])
     fi
 
-    AC_MSG_CHECKING([for $path kernel version])
-    patchlevel=`sed -n 's/^PATCHLEVEL = //p' "$path/Makefile"`
-    sublevel=`sed -n 's/^SUBLEVEL = //p' "$path/Makefile"`
+    # Debian breaks kernel headers into "source" header and "build" headers.
+    # We want the source headers, but $KBUILD26 gives us the "build" headers.
+    # Use heuristics to find the source headers.
+    AC_MSG_CHECKING([for Linux 2.6 source directory])
+    KSRC26=$KBUILD26
+    if test ! -e $KSRC26/include/linux/kernel.h; then
+      KSRC26=`(cd $KBUILD26 && pwd -P) | sed 's,-[[^-]]*$,-common,'`
+      if test ! -e $KSRC26/include/linux/kernel.h; then
+        AC_MSG_ERROR([cannot find source directory])
+      fi
+    fi
+    AC_MSG_RESULT([$KSRC26])
+
+    AC_MSG_CHECKING([for kernel version])
+    patchlevel=`sed -n 's/^PATCHLEVEL = //p' "$KSRC26/Makefile"`
+    sublevel=`sed -n 's/^SUBLEVEL = //p' "$KSRC26/Makefile"`
+    if test -z "$patchlevel" || test -z "$sublevel"; then
+       AC_ERROR([cannot determine kernel version])
+    fi
     AC_MSG_RESULT([2.$patchlevel.$sublevel])
-    if test "2.$patchlevel" != '$2'; then
-       AC_ERROR([Linux kernel source in $path is not version $2])
+    if test "2.$patchlevel" != '2.6'; then
+       if test "$BUILD26" = "$KSRC26"; then
+         AC_ERROR([Linux kernel in $KBUILD26 is not version 2.6])
+       else
+         AC_ERROR([Linux kernel in build tree $KBUILD26 (source tree $KSRC26) is not version 2.6])
+       fi
     fi
-    if ! test -e "$path"/include/linux/version.h || \
-       ! test -e "$path"/include/linux/autoconf.h; then
-       AC_MSG_ERROR([Linux kernel source in $path is not configured])
+    if ! test -e "$KBUILD26"/include/linux/version.h || \
+       ! test -e "$KBUILD26"/include/linux/autoconf.h; then
+       AC_MSG_ERROR([Linux kernel source in $KBUILD26 is not configured])
     fi
-    m4_if($2, [2.6], [OVS_CHECK_LINUX26_COMPAT])
+    OVS_CHECK_LINUX26_COMPAT
   fi
-  AM_CONDITIONAL($4, test -n "$path")
+  AM_CONDITIONAL(L26_ENABLED, test -n "$KBUILD26")
 ])
 
 dnl OVS_GREP_IFELSE(FILE, REGEX, IF-MATCH, IF-NO-MATCH)
@@ -103,7 +124,7 @@ AC_DEFUN([OVS_CHECK_LOG2_H], [
 dnl OVS_CHECK_LINUX26_COMPAT
 dnl
 dnl Runs various Autoconf checks on the Linux 2.6 kernel source in
-dnl the directory in $KSRC26.
+dnl the directory in $KBUILD26.
 AC_DEFUN([OVS_CHECK_LINUX26_COMPAT], [
   rm -f datapath/linux-2.6/kcompat.h.new
   mkdir -p datapath/linux-2.6
@@ -119,6 +140,10 @@ AC_DEFUN([OVS_CHECK_LINUX26_COMPAT], [
                   [OVS_DEFINE([HAVE_NLA_NUL_STRING])])
   OVS_GREP_IFELSE([$KSRC26/include/linux/err.h], [ERR_CAST],
                   [OVS_DEFINE([HAVE_ERR_CAST])])
+  OVS_GREP_IFELSE([$KSRC26/include/net/checksum.h], [csum_unfold],
+                  [OVS_DEFINE([HAVE_CSUM_UNFOLD])])
+  OVS_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [skb_cow],
+                  [OVS_DEFINE([HAVE_SKB_COW])])
   OVS_CHECK_LOG2_H
   OVS_CHECK_VETH
   if cmp -s datapath/linux-2.6/kcompat.h.new \
index 400ea78..300b21e 100644 (file)
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 AC_PREREQ(2.63)
-AC_INIT(openvswitch, 0.90.5, ovs-bugs@openvswitch.org)
+AC_INIT(openvswitch, 0.90.6, ovs-bugs@openvswitch.org)
 NX_BUILDNR
 AC_CONFIG_SRCDIR([datapath/datapath.c])
 AC_CONFIG_MACRO_DIR([m4])
@@ -78,7 +78,7 @@ OVS_ENABLE_OPTION([-Wno-override-init])
 
 AC_ARG_VAR(KARCH, [Kernel Architecture String])
 AC_SUBST(KARCH)
-OVS_CHECK_LINUX(l26, 2.6, KSRC26, L26_ENABLED)
+OVS_CHECK_LINUX26
 
 AC_CONFIG_FILES([Makefile 
 datapath/Makefile 
index 8a3e8ab..4f23c83 100644 (file)
 #include "actions.h"
 #include "openvswitch/datapath-protocol.h"
 
-struct sk_buff *
-make_writable(struct sk_buff *skb, gfp_t gfp)
+static struct sk_buff *
+make_writable(struct sk_buff *skb, unsigned min_headroom, gfp_t gfp)
 {
        if (skb_shared(skb) || skb_cloned(skb)) {
-               struct sk_buff *nskb = skb_copy(skb, gfp);
+               struct sk_buff *nskb;
+               unsigned headroom = max(min_headroom, skb_headroom(skb));
+
+               nskb = skb_copy_expand(skb, headroom, skb_tailroom(skb), gfp);
                if (nskb) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+                       /* Before 2.6.24 these fields were not copied when
+                        * doing an skb_copy_expand. */
+                       nskb->ip_summed = skb->ip_summed;
+                       nskb->csum = skb->csum;
+#endif
+#if defined(CONFIG_XEN) && LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
+                       /* These fields are copied in skb_clone but not in
+                        * skb_copy or related functions.  We need to manually
+                        * copy them over here. */
+                       nskb->proto_data_valid = skb->proto_data_valid;
+                       nskb->proto_csum_blank = skb->proto_csum_blank;
+#endif
                        kfree_skb(skb);
                        return nskb;
                }
@@ -80,7 +96,7 @@ modify_vlan_tci(struct datapath *dp, struct sk_buff *skb,
                mask = VLAN_PCP_MASK;
        }
 
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, VLAN_HLEN, gfp);
        if (!skb)
                return ERR_PTR(-ENOMEM);
 
@@ -168,7 +184,7 @@ modify_vlan_tci(struct datapath *dp, struct sk_buff *skb,
 static struct sk_buff *strip_vlan(struct sk_buff *skb,
                                  struct odp_flow_key *key, gfp_t gfp)
 {
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, 0, gfp);
        if (skb) {
                vlan_pull_tag(skb);
                key->dl_vlan = htons(ODP_VLAN_NONE);
@@ -180,7 +196,7 @@ static struct sk_buff *set_dl_addr(struct sk_buff *skb,
                                   const struct odp_action_dl_addr *a,
                                   gfp_t gfp)
 {
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, 0, gfp);
        if (skb) {
                struct ethhdr *eh = eth_hdr(skb);
                memcpy(a->type == ODPAT_SET_DL_SRC ? eh->h_source : eh->h_dest,
@@ -216,7 +232,7 @@ static struct sk_buff *set_nw_addr(struct sk_buff *skb,
        if (key->dl_type != htons(ETH_P_IP))
                return skb;
 
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, 0, gfp);
        if (skb) {
                struct iphdr *nh = ip_hdr(skb);
                u32 *f = a->type == ODPAT_SET_NW_SRC ? &nh->saddr : &nh->daddr;
@@ -253,14 +269,14 @@ set_tp_port(struct sk_buff *skb, struct odp_flow_key *key,
        else
                return skb;
 
-       skb = make_writable(skb, gfp);
+       skb = make_writable(skb, 0, gfp);
        if (skb) {
                struct udphdr *th = udp_hdr(skb);
                u16 *f = a->type == ODPAT_SET_TP_SRC ? &th->source : &th->dest;
                u16 old = *f;
                u16 new = a->tp_port;
-               update_csum((u16*)((u8*)skb->data + check_ofs),
-                           skb, old, new, 1);
+               update_csum((u16*)(skb_transport_header(skb) + check_ofs), 
+                               skb, old, new, 1);
                *f = new;
        }
        return skb;
@@ -307,7 +323,7 @@ do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
        dev = skb->dev = p->dev;
        if (is_dp_dev(dev))
                dp_dev_recv(dev, skb);
-        else
+       else
                dp_xmit_skb(skb);
        return;
 
index bda6d55..065c0e4 100644 (file)
@@ -16,7 +16,6 @@ struct sk_buff;
 struct odp_flow_key;
 union odp_action;
 
-struct sk_buff *make_writable(struct sk_buff *, gfp_t gfp);
 int dp_xmit_skb(struct sk_buff *);
 int execute_actions(struct datapath *dp, struct sk_buff *skb,
                    struct odp_flow_key *key,
index b8ef104..4c39767 100644 (file)
@@ -232,7 +232,7 @@ static int create_dp(int dp_idx, const char __user *devnamep)
        if (!dp->table)
                goto err_free_dp;
 
-       /* Setup our datapath device */
+       /* Set up our datapath device. */
        dp_dev = dp_dev_create(dp, devname, ODPP_LOCAL);
        err = PTR_ERR(dp_dev);
        if (IS_ERR(dp_dev))
@@ -782,7 +782,8 @@ static int validate_actions(const struct sw_flow_actions *actions)
                        break;
 
                case ODPAT_SET_VLAN_PCP:
-                       if (a->vlan_pcp.vlan_pcp & ~VLAN_PCP_MASK)
+                       if (a->vlan_pcp.vlan_pcp
+                           & ~(VLAN_PCP_MASK >> VLAN_PCP_SHIFT))
                                return -EINVAL;
                        break;
 
@@ -974,13 +975,18 @@ static int put_actions(const struct sw_flow *flow, struct odp_flow __user *ufp)
        return 0;
 }
 
-static int answer_query(struct sw_flow *flow, struct odp_flow __user *ufp)
+static int answer_query(struct sw_flow *flow, u32 query_flags,
+                       struct odp_flow __user *ufp)
 {
        struct odp_flow_stats stats;
        unsigned long int flags;
 
        spin_lock_irqsave(&flow->lock, flags);
        get_stats(flow, &stats);
+
+       if (query_flags & ODPFF_ZERO_TCP_FLAGS) {
+               flow->tcp_flags = 0;
+       }
        spin_unlock_irqrestore(&flow->lock, flags);
 
        if (__copy_to_user(&ufp->stats, &stats, sizeof(struct odp_flow_stats)))
@@ -1015,7 +1021,7 @@ static int del_flow(struct datapath *dp, struct odp_flow __user *ufp)
         * we get completely accurate stats, but that blows our performance,
         * badly. */
        dp->n_flows--;
-       error = answer_query(flow, ufp);
+       error = answer_query(flow, 0, ufp);
        flow_deferred_free(flow);
 
 error:
@@ -1040,7 +1046,7 @@ static int query_flows(struct datapath *dp, const struct odp_flowvec *flowvec)
                if (!flow)
                        error = __put_user(ENOENT, &ufp->stats.error);
                else
-                       error = answer_query(flow, ufp);
+                       error = answer_query(flow, uf.flags, ufp);
                if (error)
                        return -EFAULT;
        }
@@ -1061,7 +1067,7 @@ static int list_flow(struct sw_flow *flow, void *cbdata_)
 
        if (__copy_to_user(&ufp->key, &flow->key, sizeof flow->key))
                return -EFAULT;
-       error = answer_query(flow, ufp);
+       error = answer_query(flow, 0, ufp);
        if (error)
                return error;
 
@@ -1161,9 +1167,9 @@ static int do_execute(struct datapath *dp, const struct odp_execute *executep)
        skb_reset_mac_header(skb);
        eth = eth_hdr(skb);
 
-    /* Normally, setting the skb 'protocol' field would be handled by a
-     * call to eth_type_trans(), but it assumes there's a sending
-     * device, which we may not have. */
+       /* Normally, setting the skb 'protocol' field would be handled by a
+        * call to eth_type_trans(), but it assumes there's a sending
+        * device, which we may not have. */
        if (ntohs(eth->h_proto) >= 1536)
                skb->protocol = eth->h_proto;
        else
@@ -1371,6 +1377,16 @@ get_port_group(struct datapath *dp, struct odp_port_group *upg)
        return 0;
 }
 
+static int get_listen_mask(const struct file *f)
+{
+       return (long)f->private_data;
+}
+
+static void set_listen_mask(struct file *f, int listen_mask)
+{
+       f->private_data = (void*)(long)listen_mask;
+}
+
 static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                           unsigned long argp)
 {
@@ -1426,7 +1442,7 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                break;
 
        case ODP_GET_LISTEN_MASK:
-               err = put_user((int)f->private_data, (int __user *)argp);
+               err = put_user(get_listen_mask(f), (int __user *)argp);
                break;
 
        case ODP_SET_LISTEN_MASK:
@@ -1437,7 +1453,7 @@ static long openvswitch_ioctl(struct file *f, unsigned int cmd,
                if (listeners & ~ODPL_ALL)
                        break;
                err = 0;
-               f->private_data = (void*)listeners;
+               set_listen_mask(f, listeners);
                break;
 
        case ODP_PORT_QUERY:
@@ -1503,7 +1519,7 @@ ssize_t openvswitch_read(struct file *f, char __user *buf, size_t nbytes,
                      loff_t *ppos)
 {
        /* XXX is there sufficient synchronization here? */
-       int listeners = (int) f->private_data;
+       int listeners = get_listen_mask(f);
        int dp_idx = iminor(f->f_dentry->d_inode);
        struct datapath *dp = get_dp(dp_idx);
        struct sk_buff *skb;
@@ -1543,7 +1559,7 @@ ssize_t openvswitch_read(struct file *f, char __user *buf, size_t nbytes,
                }
        }
 success:
-       copy_bytes = min(skb->len, nbytes);
+       copy_bytes = min_t(size_t, skb->len, nbytes);
        iov.iov_base = buf;
        iov.iov_len = copy_bytes;
        retval = skb_copy_datagram_iovec(skb, 0, &iov, iov.iov_len);
@@ -1565,7 +1581,7 @@ static unsigned int openvswitch_poll(struct file *file, poll_table *wait)
        if (dp) {
                mask = 0;
                poll_wait(file, &dp->waitqueue, wait);
-               if (dp_has_packet_of_interest(dp, (int)file->private_data))
+               if (dp_has_packet_of_interest(dp, get_listen_mask(file)))
                        mask |= POLLIN | POLLRDNORM;
        } else {
                mask = POLLIN | POLLRDNORM | POLLHUP;
index d28250a..9b4c438 100644 (file)
@@ -24,6 +24,7 @@
 /* Mask for the priority bits in a vlan header.  If we ever merge upstream
  * then this should go into include/linux/if_vlan.h. */
 #define VLAN_PCP_MASK 0xe000
+#define VLAN_PCP_SHIFT 13
 
 #define DP_MAX_PORTS 1024
 #define DP_MAX_GROUPS 16
index 008f3f6..284a6b5 100644 (file)
@@ -157,22 +157,47 @@ static void dp_dev_free(struct net_device *netdev)
        free_netdev(netdev);
 }
 
+static int dp_dev_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       if (dp_ioctl_hook)
+               return dp_ioctl_hook(dev, ifr, cmd);
+       return -EOPNOTSUPP;
+}
+
+#ifdef HAVE_NET_DEVICE_OPS
+static const struct net_device_ops dp_dev_netdev_ops = {
+       .ndo_init = dp_dev_init,
+       .ndo_open = dp_dev_open,
+       .ndo_stop = dp_dev_stop,
+       .ndo_start_xmit = dp_dev_xmit,
+       .ndo_set_mac_address = dp_dev_mac_addr,
+       .ndo_do_ioctl = dp_dev_do_ioctl,
+       .ndo_change_mtu = dp_dev_change_mtu,
+       .ndo_get_stats = dp_dev_get_stats,
+};
+#endif
+
 static void
 do_setup(struct net_device *netdev)
 {
        ether_setup(netdev);
 
-       netdev->do_ioctl = dp_ioctl_hook;
+#ifdef HAVE_NET_DEVICE_OPS
+       netdev->netdev_ops = &dp_dev_netdev_ops;
+#else
+       netdev->do_ioctl = dp_dev_do_ioctl;
        netdev->get_stats = dp_dev_get_stats;
        netdev->hard_start_xmit = dp_dev_xmit;
        netdev->open = dp_dev_open;
-       SET_ETHTOOL_OPS(netdev, &dp_ethtool_ops);
        netdev->stop = dp_dev_stop;
-       netdev->tx_queue_len = 0;
        netdev->set_mac_address = dp_dev_mac_addr;
        netdev->change_mtu = dp_dev_change_mtu;
        netdev->init = dp_dev_init;
+#endif
+
        netdev->destructor = dp_dev_free;
+       SET_ETHTOOL_OPS(netdev, &dp_ethtool_ops);
+       netdev->tx_queue_len = 0;
 
        netdev->flags = IFF_BROADCAST | IFF_MULTICAST;
        netdev->features = NETIF_F_LLTX; /* XXX other features? */
@@ -233,5 +258,9 @@ void dp_dev_destroy(struct net_device *netdev)
 
 int is_dp_dev(struct net_device *netdev) 
 {
+#ifdef HAVE_NET_DEVICE_OPS
+       return netdev->netdev_ops == &dp_dev_netdev_ops;
+#else
        return netdev->open == dp_dev_open;
+#endif
 }
index ae60617..b2de023 100644 (file)
@@ -293,22 +293,22 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct odp_flow_key *key)
 
                arp = (struct arp_eth_header *)skb_network_header(skb);
 
-        if (arp->ar_hrd == htons(1)
-                && arp->ar_pro == htons(ETH_P_IP)
-                && arp->ar_hln == ETH_ALEN
-                && arp->ar_pln == 4) {
-
-            /* We only match on the lower 8 bits of the opcode. */
-            if (ntohs(arp->ar_op) <= 0xff) {
-                key->nw_proto = ntohs(arp->ar_op);
-            }
-
-            if (key->nw_proto == ARPOP_REQUEST 
-                    || key->nw_proto == ARPOP_REPLY) {
-                memcpy(&key->nw_src, arp->ar_sip, sizeof(key->nw_src));
-                memcpy(&key->nw_dst, arp->ar_tip, sizeof(key->nw_dst));
-            }
-        }
+               if (arp->ar_hrd == htons(1)
+                               && arp->ar_pro == htons(ETH_P_IP)
+                               && arp->ar_hln == ETH_ALEN
+                               && arp->ar_pln == 4) {
+
+                       /* We only match on the lower 8 bits of the opcode. */
+                       if (ntohs(arp->ar_op) <= 0xff) {
+                               key->nw_proto = ntohs(arp->ar_op);
+                       }
+
+                       if (key->nw_proto == ARPOP_REQUEST 
+                                       || key->nw_proto == ARPOP_REPLY) {
+                               memcpy(&key->nw_src, arp->ar_sip, sizeof(key->nw_src));
+                               memcpy(&key->nw_dst, arp->ar_tip, sizeof(key->nw_dst));
+                       }
+               }
        } else {
                skb_reset_transport_header(skb);
        }
@@ -332,16 +332,3 @@ void flow_exit(void)
 {
        kmem_cache_destroy(flow_cache);
 }
-
-void print_flow(const struct odp_flow_key *key)
-{
-#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
-#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5]
-    printk("port%04x:vlan%d mac"MAC_FMT"->"MAC_FMT" "
-          "type%04x proto%d ip%x->%x port%d->%d\n",
-          key->in_port, ntohs(key->dl_vlan),
-          MAC_ARG(key->dl_src), MAC_ARG(key->dl_dst),
-          ntohs(key->dl_type), key->nw_proto,
-          key->nw_src, key->nw_dst,
-          ntohs(key->tp_src), ntohs(key->tp_dst));
-}
index a825366..44cc3a6 100644 (file)
@@ -49,8 +49,6 @@ void flow_deferred_free_acts(struct sw_flow_actions *);
 int flow_extract(struct sk_buff *, u16 in_port, struct odp_flow_key *);
 void flow_used(struct sw_flow *, struct sk_buff *);
 
-void print_flow(const struct odp_flow_key *);
-
 int flow_init(void);
 void flow_exit(void);
 
index 0005ec4..967e2f7 100644 (file)
@@ -2,7 +2,7 @@
 export builddir = @abs_builddir@
 export srcdir = @abs_srcdir@
 export top_srcdir = @abs_top_srcdir@
-export KSRC = @KSRC26@
+export KSRC = @KBUILD26@
 export VERSION = @VERSION@
 export BUILD_VETH = @BUILD_VETH@
 
index e5aa51d..7892583 100644 (file)
@@ -4,6 +4,7 @@ openvswitch_sources += \
 openvswitch_headers += \
        linux-2.6/compat-2.6/compat26.h \
        linux-2.6/compat-2.6/include/asm-generic/bug.h \
+       linux-2.6/compat-2.6/include/linux/cpumask.h \
        linux-2.6/compat-2.6/include/linux/dmi.h \
        linux-2.6/compat-2.6/include/linux/err.h \
        linux-2.6/compat-2.6/include/linux/icmp.h \
@@ -13,8 +14,8 @@ openvswitch_headers += \
        linux-2.6/compat-2.6/include/linux/jiffies.h \
        linux-2.6/compat-2.6/include/linux/kernel.h \
        linux-2.6/compat-2.6/include/linux/kobject.h \
-       linux-2.6/compat-2.6/include/linux/log2.h \
        linux-2.6/compat-2.6/include/linux/lockdep.h \
+       linux-2.6/compat-2.6/include/linux/log2.h \
        linux-2.6/compat-2.6/include/linux/mutex.h \
        linux-2.6/compat-2.6/include/linux/netdevice.h \
        linux-2.6/compat-2.6/include/linux/netfilter_bridge.h \
index 666ef85..2831721 100644 (file)
@@ -42,7 +42,7 @@ static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb,
 #define NET_SKB_PAD    16
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+#ifndef HAVE_SKB_COW
 static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom,
                             int cloned)
 {
@@ -63,7 +63,7 @@ static inline int skb_cow_head(struct sk_buff *skb, unsigned int headroom)
 {
        return __skb_cow(skb, headroom, skb_header_cloned(skb));
 }
-#endif  /* linux < 2.6.23 */
+#endif  /* !HAVE_SKB_COW */
 
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
index c64c6bd..3b6debb 100644 (file)
@@ -3,14 +3,11 @@
 
 #include_next <net/checksum.h>
 
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-
+#ifndef HAVE_CSUM_UNFOLD
 static inline __wsum csum_unfold(__sum16 n)
 {
        return (__force __wsum)n;
 }
-
-#endif /* linux kernel < 2.6.20 */
+#endif /* !HAVE_CSUM_UNFOLD */
 
 #endif /* checksum.h */
index a45cc2a..3da57e0 100644 (file)
@@ -6,6 +6,6 @@
         missingok
         rotate 30
         postrotate
-                ovs-appctl --target /var/run/ovs-openflowd.pid --reopen
+                ovs-appctl --target=ovs-openflowd vlog/reopen
         endscript
 }
index 0f6640e..cd1f352 100644 (file)
@@ -1486,7 +1486,7 @@ static int menu_show(const struct menu *, int start, bool select);
 static void cmd_shell(const struct dict *);
 static void cmd_show_version(const struct dict *);
 static void cmd_configure(const struct dict *);
-static void cmd_setup_pki(const struct dict *);
+static void cmd_set_up_pki(const struct dict *);
 static void cmd_browse_status(const struct dict *);
 static void cmd_show_motto(const struct dict *);
 
@@ -1542,7 +1542,7 @@ menu(const struct dict *dict)
     menu_add_item(&menu, "Exit");
     menu_add_item(&menu, "Show Version")->f = cmd_show_version;
     menu_add_item(&menu, "Configure")->f = cmd_configure;
-    menu_add_item(&menu, "Setup PKI")->f = cmd_setup_pki;
+    menu_add_item(&menu, "Set up PKI")->f = cmd_set_up_pki;
     if (debug_mode) {
         menu_add_item(&menu, "Browse Status")->f = cmd_browse_status;
         menu_add_item(&menu, "Shell")->f = cmd_shell;
@@ -2889,7 +2889,7 @@ cmd_configure(const struct dict *dict UNUSED)
 }
 
 static void
-cmd_setup_pki(const struct dict *dict UNUSED)
+cmd_set_up_pki(const struct dict *dict UNUSED)
 {
     static const char def_privkey_file[]
         = "/etc/openflow-switch/of0-privkey.pem";
index 1d70aab..1f18370 100644 (file)
@@ -351,7 +351,7 @@ struct ofp_action_vlan_pcp {
     uint8_t vlan_pcp;               /* VLAN priority. */
     uint8_t pad[3];
 };
-OFP_ASSERT(sizeof(struct ofp_action_vlan_vid) == 8);
+OFP_ASSERT(sizeof(struct ofp_action_vlan_pcp) == 8);
 
 /* Action structure for OFPAT_SET_DL_SRC/DST. */
 struct ofp_action_dl_addr {
index 04423d9..ab7eb9e 100644 (file)
@@ -165,11 +165,15 @@ struct odp_flow_key {
     __u8   reserved;             /* Pad to 64 bits. */
 };
 
+/* Flags for ODP_FLOW. */
+#define ODPFF_ZERO_TCP_FLAGS (1 << 0) /* Zero the TCP flags. */
+
 struct odp_flow {
     struct odp_flow_stats stats;
     struct odp_flow_key key;
     union odp_action *actions;
     __u32 n_actions;
+    __u32 flags;
 };
 
 /* Flags for ODP_FLOW_PUT. */
index 0999a08..2f47809 100644 (file)
@@ -42,7 +42,7 @@ get_max_stack(void)
     for (line_number = 1; fgets(line, sizeof line, f); line_number++) {
         if (strstr(line, "[stack]")) {
             uintptr_t end;
-            if (sscanf(line, "%*"SCNxPTR"-%"SCNxPTR, &end) != 1) {
+            if (sscanf(line, "%*x-%"SCNxPTR, &end) != 1) {
                 VLOG_WARN("%s:%d: parse error", file_name, line_number);
                 continue;
             }
@@ -73,6 +73,10 @@ stack_low(void)
     uintptr_t low;
     asm("movl %%esp,%0" : "=g" (low));
     return low;
+#elif __x86_64__
+    uintptr_t low;
+    asm("movq %%rsp,%0" : "=g" (low));
+    return low;
 #else
     /* This causes a warning in GCC that cannot be disabled, so use it only on
      * non-x86. */
index 225827e..d0170b6 100644 (file)
--- a/lib/cfg.c
+++ b/lib/cfg.c
@@ -136,7 +136,7 @@ cfg_set_file(const char *file_name)
     slash = strrchr(file_name, '/');
     if (slash) {
         lock_name = xasprintf("%.*s/.%s.~lock~",
-                              slash - file_name, file_name, slash + 1);
+                              (int) (slash - file_name), file_name, slash + 1);
     } else {
         lock_name = xasprintf(".%s.~lock~", file_name);
     }
index 1e3f002..a35c639 100644 (file)
@@ -23,6 +23,7 @@
 #include <unistd.h>
 #include "fatal-signal.h"
 #include "dirs.h"
+#include "timeval.h"
 #include "util.h"
 
 #define THIS_MODULE VLM_daemon
@@ -222,6 +223,7 @@ daemonize(void)
             if (chdir_) {
                 chdir("/");
             }
+            time_postfork();
             break;
 
         case -1:
index 8216d18..2bf329f 100644 (file)
@@ -419,7 +419,7 @@ dpif_linux_recv(struct dpif *dpif_, struct ofpbuf **bufp)
             return 0;
         } else {
             VLOG_WARN_RL(&error_rl, "%s: discarding message truncated "
-                         "from %zu bytes to %d",
+                         "from %"PRIu32" bytes to %d",
                          dpif_name(dpif_), msg->length, retval);
             error = ERANGE;
         }
index 4c25f13..35724d9 100644 (file)
@@ -665,7 +665,7 @@ dp_netdev_lookup_flow(const struct dp_netdev *dp, const flow_t *key)
 }
 
 static void
-answer_flow_query(const struct dp_netdev_flow *flow,
+answer_flow_query(struct dp_netdev_flow *flow, uint32_t query_flags,
                   struct odp_flow *odp_flow)
 {
     if (flow) {
@@ -683,6 +683,11 @@ answer_flow_query(const struct dp_netdev_flow *flow,
                    n * sizeof *odp_flow->actions);
             odp_flow->n_actions = flow->n_actions;
         }
+
+        if (query_flags & ODPFF_ZERO_TCP_FLAGS) {
+            flow->tcp_ctl = 0;
+        }
+
     } else {
         odp_flow->stats.error = ENOENT;
     }
@@ -696,7 +701,8 @@ dpif_netdev_flow_get(const struct dpif *dpif, struct odp_flow flows[], int n)
 
     for (i = 0; i < n; i++) {
         struct odp_flow *odp_flow = &flows[i];
-        answer_flow_query(dp_netdev_lookup_flow(dp, &odp_flow->key), odp_flow);
+        answer_flow_query(dp_netdev_lookup_flow(dp, &odp_flow->key),
+                          odp_flow->flags, odp_flow);
     }
     return 0;
 }
@@ -852,7 +858,7 @@ dpif_netdev_flow_del(struct dpif *dpif, struct odp_flow *odp_flow)
 
     flow = dp_netdev_lookup_flow(dp, &odp_flow->key);
     if (flow) {
-        answer_flow_query(flow, odp_flow);
+        answer_flow_query(flow, 0, odp_flow);
         dp_netdev_free_flow(dp, flow);
         return 0;
     } else {
@@ -872,7 +878,7 @@ dpif_netdev_flow_list(const struct dpif *dpif, struct odp_flow flows[], int n)
         if (i >= n) {
             break;
         }
-        answer_flow_query(flow, &flows[i++]);
+        answer_flow_query(flow, 0, &flows[i++]);
     }
     return hmap_count(&dp->flow_table);
 }
index 3882eff..14e229e 100644 (file)
@@ -54,7 +54,7 @@ log_backtrace(void)
         if (!dladdr(frame[1], &addrinfo) || !addrinfo.dli_sname) {
             fprintf(stderr, "  0x%08"PRIxPTR"\n", (uintptr_t) frame[1]);
         } else {
-            fprintf(stderr, "  0x%08"PRIxPTR" (%s+0x%x)\n",
+            fprintf(stderr, "  0x%08"PRIxPTR" (%s+0x%tx)\n",
                     (uintptr_t) frame[1], addrinfo.dli_sname,
                     (char *) frame[1] - (char *) addrinfo.dli_saddr); 
         }
index c1f6240..7d4a1bd 100644 (file)
@@ -317,7 +317,7 @@ flow_to_string(const flow_t *flow)
 void
 flow_format(struct ds *ds, const flow_t *flow)
 {
-    ds_put_format(ds, "port%04x:vlan%d mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT" "
+    ds_put_format(ds, "in_port%04x:vlan%d mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT" "
                   "type%04x proto%"PRId8" ip"IP_FMT"->"IP_FMT" port%d->%d",
                   flow->in_port, ntohs(flow->dl_vlan),
                   ETH_ADDR_ARGS(flow->dl_src), ETH_ADDR_ARGS(flow->dl_dst),
index c2c4348..8d256bc 100644 (file)
@@ -141,7 +141,7 @@ log_callers(const char *format, ...)
     putc(':', file);
     backtrace_capture(&backtrace);
     for (i = 0; i < backtrace.n_frames; i++) {
-        fprintf(file, " 0x%x", backtrace.frames[i]);
+        fprintf(file, " 0x%"PRIxPTR, backtrace.frames[i]);
     }
     putc('\n', file);
 }
index c9b7d3e..3f1db14 100644 (file)
@@ -21,6 +21,7 @@
 #include <inttypes.h>
 #include <stdlib.h>
 
+#include "bitmap.h"
 #include "coverage.h"
 #include "hash.h"
 #include "list.h"
@@ -129,6 +130,7 @@ mac_learning_create(void)
         list_push_front(&ml->free, &s->lru_node);
     }
     ml->secret = random_uint32();
+    ml->non_learning_vlans = NULL;
     return ml;
 }
 
@@ -136,9 +138,36 @@ mac_learning_create(void)
 void
 mac_learning_destroy(struct mac_learning *ml)
 {
+    if (ml) {
+        bitmap_free(ml->non_learning_vlans);
+    }
     free(ml);
 }
 
+/* Provides a bitmap of VLANs which have learning disabled.  It takes
+ * ownership of the bitmap.  Returns true if the set has changed from
+ * the previous value. */
+bool
+mac_learning_set_disabled_vlans(struct mac_learning *ml, unsigned long *bitmap)
+{
+    bool ret = (bitmap == NULL
+        ? ml->non_learning_vlans != NULL
+        : (ml->non_learning_vlans == NULL
+          || !bitmap_equal(bitmap, ml->non_learning_vlans, 4096)));
+
+    bitmap_free(ml->non_learning_vlans);
+    ml->non_learning_vlans = bitmap;
+
+    return ret;
+}
+
+static bool
+is_learning_vlan(const struct mac_learning *ml, uint16_t vlan)
+{
+    return !(ml->non_learning_vlans
+            && bitmap_is_set(ml->non_learning_vlans, vlan));
+}
+
 /* Attempts to make 'ml' learn from the fact that a frame from 'src_mac' was
  * just observed arriving from 'src_port' on the given 'vlan'.
  *
@@ -156,6 +185,10 @@ mac_learning_learn(struct mac_learning *ml,
     struct mac_entry *e;
     struct list *bucket;
 
+    if (!is_learning_vlan(ml, vlan)) {
+        return 0;
+    }
+
     if (eth_addr_is_multicast(src_mac)) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 30);
         VLOG_DBG_RL(&rl, "multicast packet source "ETH_ADDR_FMT,
@@ -216,7 +249,7 @@ mac_learning_lookup_tag(const struct mac_learning *ml,
                         const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan,
                         tag_type *tag)
 {
-    if (eth_addr_is_multicast(dst)) {
+    if (eth_addr_is_multicast(dst) || !is_learning_vlan(ml, vlan)) {
         return -1;
     } else {
         struct mac_entry *e = search_bucket(mac_table_bucket(ml, dst, vlan),
index e2ee74b..ed843cd 100644 (file)
@@ -52,10 +52,13 @@ struct mac_learning {
     struct list table[MAC_HASH_SIZE]; /* Hash table. */
     struct mac_entry entries[MAC_MAX]; /* All entries. */
     uint32_t secret;            /* Secret for  */
+    unsigned long *non_learning_vlans; /* Bitmap of learning disabled VLANs. */
 };
 
 struct mac_learning *mac_learning_create(void);
 void mac_learning_destroy(struct mac_learning *);
+bool mac_learning_set_disabled_vlans(struct mac_learning *,
+                                     unsigned long *bitmap);
 tag_type mac_learning_learn(struct mac_learning *,
                             const uint8_t src[ETH_ADDR_LEN], uint16_t vlan,
                             uint16_t src_port);
index 21bc9a5..a800162 100644 (file)
@@ -111,8 +111,9 @@ format_odp_actions(struct ds *ds, const union odp_action *actions,
 void
 format_odp_flow_stats(struct ds *ds, const struct odp_flow_stats *s)
 {
-    ds_put_format(ds, "packets:%"PRIu64", bytes:%"PRIu64", used:",
-                  s->n_packets, s->n_bytes);
+    ds_put_format(ds, "packets:%llu, bytes:%llu, used:",
+                  (unsigned long long int) s->n_packets,
+                  (unsigned long long int) s->n_bytes);
     if (s->used_sec) {
         long long int used = s->used_sec * 1000 + s->used_nsec / 1000000;
         ds_put_format(ds, "%.3fs", (time_msec() - used) / 1000.0);
index 63b59dd..9c1afe4 100644 (file)
@@ -797,7 +797,11 @@ static const struct error_type error_types[] = {
     ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION),
     ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE),
     ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT),
-    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION),
+    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR),
+    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE),
+    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH),
+    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BUFFER_EMPTY),
+    ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_COOKIE),
 
     ERROR_TYPE(OFPET_BAD_ACTION),
     ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE),
@@ -805,9 +809,16 @@ static const struct error_type error_types[] = {
     ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR),
     ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE),
     ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT),
+    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT),
+    ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_TOO_MANY),
 
     ERROR_TYPE(OFPET_FLOW_MOD_FAILED),
-    ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL)
+    ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL),
+    ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND),
+
+    ERROR_TYPE(OFPET_PORT_MOD_FAILED),
+    ERROR_CODE(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT),
+    ERROR_CODE(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR)
 };
 #define N_ERROR_TYPES ARRAY_SIZE(error_types)
 
index 4330c57..967bb5c 100644 (file)
@@ -128,7 +128,7 @@ pcap_read(FILE *file, struct ofpbuf **bufp)
                                 ((len & 0x0000ff00) <<  8) |
                                 ((len & 0x000000ff) << 24));
         if (swapped_len > 0xffff) {
-            VLOG_WARN("bad packet length %"PRIu32" or %"PRIu32" "
+            VLOG_WARN("bad packet length %zu or %"PRIu32" "
                       "reading pcap file",
                       len, swapped_len);
             return EPROTO;
index aff2c33..945b5c4 100644 (file)
@@ -18,6 +18,7 @@
 #include "poll-loop.h"
 #include <assert.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <poll.h>
 #include <stdlib.h>
 #include <string.h>
@@ -122,7 +123,7 @@ log_wakeup(const struct backtrace *backtrace, const char *format, ...)
 
         ds_put_char(&ds, ':');
         for (i = 0; i < backtrace->n_frames; i++) {
-            ds_put_format(&ds, " 0x%x", backtrace->frames[i]);
+            ds_put_format(&ds, " 0x%"PRIxPTR, backtrace->frames[i]);
         }
     }
     VLOG_DBG("%s", ds_cstr(&ds));
index 3cca338..8ad8d06 100644 (file)
@@ -43,6 +43,7 @@ static struct timeval now;
 /* Time at which to die with SIGALRM (if not TIME_MIN). */
 static time_t deadline = TIME_MIN;
 
+static void set_up_timer(void);
 static void sigalrm_handler(int);
 static void refresh_if_ticked(void);
 static time_t time_add(time_t, time_t);
@@ -57,8 +58,6 @@ void
 time_init(void)
 {
     struct sigaction sa;
-    struct itimerval itimer;
-
     if (inited) {
         return;
     }
@@ -78,7 +77,15 @@ time_init(void)
         ovs_fatal(errno, "sigaction(SIGALRM) failed");
     }
 
-    /* Set up periodic timer. */
+    /* Set up periodic signal. */
+    set_up_timer();
+}
+
+static void
+set_up_timer(void)
+{
+    struct itimerval itimer;
+
     itimer.it_interval.tv_sec = 0;
     itimer.it_interval.tv_usec = TIME_UPDATE_INTERVAL * 1000;
     itimer.it_value = itimer.it_interval;
@@ -87,6 +94,17 @@ time_init(void)
     }
 }
 
+/* Set up the interval timer, to ensure that time advances even without calling
+ * time_refresh().
+ *
+ * A child created with fork() does not inherit the parent's interval timer, so
+ * this function needs to be called from the child after fork(). */
+void
+time_postfork(void)
+{
+    set_up_timer();
+}
+
 /* Forces a refresh of the current time from the kernel.  It is not usually
  * necessary to call this function, since the time will be refreshed
  * automatically at least every TIME_UPDATE_INTERVAL milliseconds. */
index 660a207..8567d75 100644 (file)
@@ -41,6 +41,7 @@ BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t));
 #define TIME_UPDATE_INTERVAL 100
 
 void time_init(void);
+void time_postfork(void);
 void time_refresh(void);
 time_t time_now(void);
 long long int time_msec(void);
index e774ffe..164a7db 100644 (file)
@@ -168,7 +168,7 @@ unixctl_command_reply(struct unixctl_conn *conn,
  * A program that (optionally) daemonizes itself should call this function
  * *after* daemonization, so that the socket name contains the pid of the
  * daemon instead of the pid of the program that exited.  (Otherwise,
- * "ovs-appctl --target <program>.pid" will fail.)
+ * "ovs-appctl --target=<program>" will fail.)
  *
  * Returns 0 if successful, otherwise a positive errno value.  If successful,
  * sets '*serverp' to the new unixctl_server, otherwise to NULL. */
index 3cd2948..b11650f 100644 (file)
@@ -1042,7 +1042,7 @@ check_ofp_message(const struct ofp_header *msg, uint8_t type, size_t size)
     if (got_size != size) {
         char *type_name = ofp_message_type_to_string(type);
         VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "received %s message of length %"PRIu16" (expected %zu)",
+                     "received %s message of length %zu (expected %zu)",
                      type_name, got_size, size);
         free(type_name);
         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
@@ -1077,7 +1077,7 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type,
     got_size = ntohs(msg->length);
     if (got_size < min_size) {
         char *type_name = ofp_message_type_to_string(type);
-        VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %"PRIu16" "
+        VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %zu "
                      "(expected at least %zu)",
                      type_name, got_size, min_size);
         free(type_name);
@@ -1086,7 +1086,7 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type,
     if ((got_size - min_size) % array_elt_size) {
         char *type_name = ofp_message_type_to_string(type);
         VLOG_WARN_RL(&bad_ofmsg_rl,
-                     "received %s message of bad length %"PRIu16": the "
+                     "received %s message of bad length %zu: the "
                      "excess over %zu (%zu) is not evenly divisible by %zu "
                      "(remainder is %zu)",
                      type_name, got_size, min_size, got_size - min_size,
@@ -1119,13 +1119,13 @@ check_ofp_packet_out(const struct ofp_header *oh, struct ofpbuf *data,
 
     actions_len = ntohs(opo->actions_len);
     if (actions_len > extra) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %zu bytes of actions "
+        VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions "
                      "but message has room for only %zu bytes",
                      actions_len, extra);
         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
     }
     if (actions_len % sizeof(union ofp_action)) {
-        VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %zu bytes of actions, "
+        VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions, "
                      "which is not a multiple of %zu",
                      actions_len, sizeof(union ofp_action));
         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
@@ -1282,7 +1282,8 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports)
         break;
 
     default:
-        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16, a->type);
+        VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16,
+                ntohs(a->type));
         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE);
     }
 
@@ -1312,7 +1313,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
 
         if (n_slots > slots_left) {
             VLOG_DBG_RL(&bad_ofmsg_rl,
-                        "action requires %u slots but only %td remain",
+                        "action requires %u slots but only %u remain",
                         n_slots, slots_left);
             return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
         }
@@ -1360,7 +1361,7 @@ normalize_match(struct ofp_match *m)
     if (wc & OFPFW_DL_TYPE) {
         m->dl_type = 0;
 
-        /* Can't sensibly m on network or transport headers if the
+        /* Can't sensibly match on network or transport headers if the
          * data link type is unknown. */
         wc |= OFPFW_NW | OFPFW_TP;
         m->nw_src = m->nw_dst = m->nw_proto = 0;
@@ -1369,7 +1370,7 @@ normalize_match(struct ofp_match *m)
         if (wc & OFPFW_NW_PROTO) {
             m->nw_proto = 0;
 
-            /* Can't sensibly m on transport headers if the network
+            /* Can't sensibly match on transport headers if the network
              * protocol is unknown. */
             wc |= OFPFW_TP;
             m->tp_src = m->tp_dst = 0;
@@ -1384,7 +1385,7 @@ normalize_match(struct ofp_match *m)
             }
         } else {
             /* Transport layer fields will always be extracted as zeros, so we
-             * can do an exact-m on those values.  */
+             * can do an exact-match on those values.  */
             wc &= ~OFPFW_TP;
             m->tp_src = m->tp_dst = 0;
         }
@@ -1396,7 +1397,7 @@ normalize_match(struct ofp_match *m)
         }
     } else {
         /* Network and transport layer fields will always be extracted as
-         * zeros, so we can do an exact-m on those values. */
+         * zeros, so we can do an exact-match on those values. */
         wc &= ~(OFPFW_NW | OFPFW_TP);
         m->nw_proto = m->nw_src = m->nw_dst = 0;
         m->tp_src = m->tp_dst = 0;
index 5c79875..86eece3 100644 (file)
@@ -7,7 +7,7 @@ Sets the logging level for \fImodule\fR in \fIfacility\fR to
 .RS
 .IP \(bu
 \fImodule\fR may be any valid module name (as displayed by the
-\fB--list\fR action on \fBovs-appctl\fR(8)), or the special name
+\fB--list\fR action on \fBovs\-appctl\fR(8)), or the special name
 \fBANY\fR to set the logging levels for all modules.
 .
 .IP \(bu
@@ -26,7 +26,7 @@ logged.  If it is omitted, \fIlevel\fR defaults to \fBdbg\fR.
 .RE
 .IP "\fBvlog/set PATTERN:\fIfacility\fB:\fIpattern\fR"
 Sets the log pattern for \fIfacility\fR to \fIpattern\fR.  Refer to
-\fBovs-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
+\fBovs\-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
 .
 .IP "\fBvlog/list\fR"
 Lists the supported logging modules and their current levels.
index 0bd8a26..8827931 100644 (file)
@@ -7,7 +7,7 @@ Sets the logging level for \fImodule\fR in \fIfacility\fR to
 .RS
 .IP \(bu
 \fImodule\fR may be any valid module name (as displayed by the
-\fB--list\fR action on \fBovs-appctl\fR(8)), or the special name
+\fB--list\fR action on \fBovs\-appctl\fR(8)), or the special name
 \fBANY\fR to set the logging levels for all modules.
 
 .IP \(bu
@@ -35,7 +35,7 @@ Sets the maximum logging verbosity level, equivalent to
 .TP
 \fB-vPATTERN:\fIfacility\fB:\fIpattern\fR, \fB--verbose=PATTERN:\fIfacility\fB:\fIpattern\fR
 Sets the log pattern for \fIfacility\fR to \fIpattern\fR.  Refer to
-\fBovs-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
+\fBovs\-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
 
 .TP
 \fB--log-file\fR[\fB=\fIfile\fR]
index 2b362bc..c39e5ca 100644 (file)
@@ -55,7 +55,7 @@
  *
  * In Open vSwitch, in-band control is implemented as "hidden" flows (in
  * that they are not visible through OpenFlow) and at a higher priority
- * than wildcarded flows can be setup by the controller.  This is done 
+ * than wildcarded flows can be set up by the controller.  This is done
  * so that the controller cannot interfere with them and possibly break 
  * connectivity with its switches.  It is possible to see all flows, 
  * including in-band ones, with the ovs-appctl "bridge/dump-flows" 
  *
  *    - Differing Controllers for Switches.  All switches must know
  *      the L3 addresses for all the controllers that other switches 
- *      may use, since rules need to be setup to allow traffic related 
+ *      may use, since rules need to be set up to allow traffic related
  *      to those controllers through.  See rules (f), (g), (h), and (i).
  *
  *    - Differing Routes for Switches.  In order for the switch to 
@@ -358,8 +358,8 @@ drop_flow(struct in_band *in_band, int rule_idx)
 
 /* out_port and fixed_fields are assumed never to change. */
 static void
-setup_flow(struct in_band *in_band, int rule_idx, const flow_t *flow,
-           uint32_t fixed_fields, uint16_t out_port)
+set_up_flow(struct in_band *in_band, int rule_idx, const flow_t *flow,
+            uint32_t fixed_fields, uint16_t out_port)
 {
     struct ib_rule *rule = &in_band->rules[rule_idx];
 
@@ -481,28 +481,28 @@ in_band_run(struct in_band *in_band)
         flow.nw_proto = IP_TYPE_UDP;
         flow.tp_src = htons(DHCP_CLIENT_PORT);
         flow.tp_dst = htons(DHCP_SERVER_PORT);
-        setup_flow(in_band, IBR_FROM_LOCAL_DHCP, &flow,
-                   (OFPFW_IN_PORT | OFPFW_DL_TYPE | OFPFW_DL_SRC
-                    | OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST), 
-                   OFPP_NORMAL);
+        set_up_flow(in_band, IBR_FROM_LOCAL_DHCP, &flow,
+                    (OFPFW_IN_PORT | OFPFW_DL_TYPE | OFPFW_DL_SRC
+                     | OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST), 
+                    OFPP_NORMAL);
 
         /* Allow the connection's interface to receive directed ARP traffic. */
         memset(&flow, 0, sizeof flow);
         flow.dl_type = htons(ETH_TYPE_ARP);
         memcpy(flow.dl_dst, local_mac, ETH_ADDR_LEN);
         flow.nw_proto = ARP_OP_REPLY;
-        setup_flow(in_band, IBR_TO_LOCAL_ARP, &flow,
-                   (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), 
-                   OFPP_NORMAL);
+        set_up_flow(in_band, IBR_TO_LOCAL_ARP, &flow,
+                    (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), 
+                    OFPP_NORMAL);
 
         /* Allow the connection's interface to be the source of ARP traffic. */
         memset(&flow, 0, sizeof flow);
         flow.dl_type = htons(ETH_TYPE_ARP);
         memcpy(flow.dl_src, local_mac, ETH_ADDR_LEN);
         flow.nw_proto = ARP_OP_REQUEST;
-        setup_flow(in_band, IBR_FROM_LOCAL_ARP, &flow,
-                   (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO),
-                   OFPP_NORMAL);
+        set_up_flow(in_band, IBR_FROM_LOCAL_ARP, &flow,
+                    (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO),
+                    OFPP_NORMAL);
     } else {
         drop_flow(in_band, IBR_TO_LOCAL_ARP);
         drop_flow(in_band, IBR_FROM_LOCAL_ARP);
@@ -514,18 +514,18 @@ in_band_run(struct in_band *in_band)
         flow.dl_type = htons(ETH_TYPE_ARP);
         memcpy(flow.dl_dst, remote_mac, ETH_ADDR_LEN);
         flow.nw_proto = ARP_OP_REPLY;
-        setup_flow(in_band, IBR_TO_REMOTE_ARP, &flow,
-                   (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), 
-                   OFPP_NORMAL);
+        set_up_flow(in_band, IBR_TO_REMOTE_ARP, &flow,
+                    (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO), 
+                    OFPP_NORMAL);
 
        /* Allow ARP requests from the remote side's MAC. */
         memset(&flow, 0, sizeof flow);
         flow.dl_type = htons(ETH_TYPE_ARP);
         memcpy(flow.dl_src, remote_mac, ETH_ADDR_LEN);
         flow.nw_proto = ARP_OP_REQUEST;
-        setup_flow(in_band, IBR_FROM_REMOTE_ARP, &flow,
-                   (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO), 
-                   OFPP_NORMAL);
+        set_up_flow(in_band, IBR_FROM_REMOTE_ARP, &flow,
+                    (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO), 
+                    OFPP_NORMAL);
     } else {
         drop_flow(in_band, IBR_TO_REMOTE_ARP);
         drop_flow(in_band, IBR_FROM_REMOTE_ARP);
@@ -537,18 +537,18 @@ in_band_run(struct in_band *in_band)
         flow.dl_type = htons(ETH_TYPE_ARP);
         flow.nw_proto = ARP_OP_REPLY;
         flow.nw_dst = controller_ip;
-        setup_flow(in_band, IBR_TO_CTL_ARP, &flow,
-                   (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK),
-                   OFPP_NORMAL);
+        set_up_flow(in_band, IBR_TO_CTL_ARP, &flow,
+                    (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK),
+                    OFPP_NORMAL);
 
        /* Allow ARP requests from the controller's IP. */
         memset(&flow, 0, sizeof flow);
         flow.dl_type = htons(ETH_TYPE_ARP);
         flow.nw_proto = ARP_OP_REQUEST;
         flow.nw_src = controller_ip;
-        setup_flow(in_band, IBR_FROM_CTL_ARP, &flow,
-                   (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK),
-                   OFPP_NORMAL);
+        set_up_flow(in_band, IBR_FROM_CTL_ARP, &flow,
+                    (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK),
+                    OFPP_NORMAL);
      
         /* OpenFlow traffic to or from the controller.
          *
@@ -562,12 +562,12 @@ in_band_run(struct in_band *in_band)
         flow.nw_dst = controller_ip;
         flow.tp_src = htons(OFP_TCP_PORT);
         flow.tp_dst = htons(OFP_TCP_PORT);
-        setup_flow(in_band, IBR_TO_CTL_OFP, &flow,
-                   (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK 
-                    | OFPFW_TP_DST), OFPP_NORMAL);
-        setup_flow(in_band, IBR_FROM_CTL_OFP, &flow,
-                   (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK
-                    | OFPFW_TP_SRC), OFPP_NORMAL);
+        set_up_flow(in_band, IBR_TO_CTL_OFP, &flow,
+                    (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_DST_MASK 
+                     | OFPFW_TP_DST), OFPP_NORMAL);
+        set_up_flow(in_band, IBR_FROM_CTL_OFP, &flow,
+                    (OFPFW_DL_TYPE | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK
+                     | OFPFW_TP_SRC), OFPP_NORMAL);
     } else {
         drop_flow(in_band, IBR_TO_CTL_ARP);
         drop_flow(in_band, IBR_FROM_CTL_ARP);
index 7912b4b..0505cd3 100644 (file)
@@ -37,6 +37,8 @@
 
 #define NETFLOW_V5_VERSION 5
 
+static const int ACTIVE_TIMEOUT_DEFAULT = 600;
+
 /* Every NetFlow v5 message contains the header that follows.  This is
  * followed by up to thirty records that describe a terminating flow.
  * We only send a single record per NetFlow message.
@@ -100,6 +102,8 @@ struct netflow {
                                    * bits of the interface fields. */
     uint32_t netflow_cnt;         /* Flow sequence number for NetFlow. */
     struct ofpbuf packet;         /* NetFlow packet being accumulated. */
+    long long int active_timeout; /* Timeout for flows that are still active. */
+    long long int reconfig_time;  /* When we reconfigured the timeouts. */
 };
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -160,14 +164,19 @@ open_collector(char *dst)
 }
 
 void
-netflow_expire(struct netflow *nf, const struct ofexpired *expired)
+netflow_expire(struct netflow *nf, struct netflow_flow *nf_flow,
+               struct ofexpired *expired)
 {
     struct netflow_v5_header *nf_hdr;
     struct netflow_v5_record *nf_rec;
     struct timeval now;
 
-    /* NetFlow only reports on IP packets. */
-    if (expired->flow.dl_type != htons(ETH_TYPE_IP)) {
+    nf_flow->last_expired += nf->active_timeout;
+
+    /* NetFlow only reports on IP packets and we should only report flows
+     * that actually have traffic. */
+    if (expired->flow.dl_type != htons(ETH_TYPE_IP) ||
+        expired->packet_count - nf_flow->packet_count_off == 0) {
         return;
     }
 
@@ -196,15 +205,17 @@ netflow_expire(struct netflow *nf, const struct ofexpired *expired)
     if (nf->add_id_to_iface) {
         uint16_t iface = (nf->engine_id & 0x7f) << 9;
         nf_rec->input = htons(iface | (expired->flow.in_port & 0x1ff));
-        nf_rec->output = htons(iface);
+        nf_rec->output = htons(iface | (nf_flow->output_iface & 0x1ff));
     } else {
         nf_rec->input = htons(expired->flow.in_port);
-        nf_rec->output = htons(0);
+        nf_rec->output = htons(nf_flow->output_iface);
     }
-    nf_rec->packet_count = htonl(MIN(expired->packet_count, UINT32_MAX));
-    nf_rec->byte_count = htonl(MIN(expired->byte_count, UINT32_MAX));
-    nf_rec->init_time = htonl(expired->created - nf->boot_time);
-    nf_rec->used_time = htonl(MAX(expired->created, expired->used)
+    nf_rec->packet_count = htonl(MIN(expired->packet_count -
+                                     nf_flow->packet_count_off, UINT32_MAX));
+    nf_rec->byte_count = htonl(MIN(expired->byte_count -
+                                   nf_flow->byte_count_off, UINT32_MAX));
+    nf_rec->init_time = htonl(nf_flow->created - nf->boot_time);
+    nf_rec->used_time = htonl(MAX(nf_flow->created, expired->used)
                              - nf->boot_time);
     if (expired->flow.nw_proto == IP_TYPE_ICMP) {
         /* In NetFlow, the ICMP type and code are concatenated and
@@ -217,9 +228,15 @@ netflow_expire(struct netflow *nf, const struct ofexpired *expired)
         nf_rec->src_port = expired->flow.tp_src;
         nf_rec->dst_port = expired->flow.tp_dst;
     }
-    nf_rec->tcp_flags = expired->tcp_flags;
+    nf_rec->tcp_flags = nf_flow->tcp_flags;
     nf_rec->ip_proto = expired->flow.nw_proto;
-    nf_rec->ip_tos = expired->ip_tos;
+    nf_rec->ip_tos = nf_flow->ip_tos;
+
+    /* Update flow tracking data. */
+    nf_flow->created = 0;
+    nf_flow->packet_count_off = expired->packet_count;
+    nf_flow->byte_count_off = expired->byte_count;
+    nf_flow->tcp_flags = 0;
 
     /* NetFlow messages are limited to 30 records. */
     if (ntohs(nf_hdr->count) >= 30) {
@@ -259,15 +276,21 @@ clear_collectors(struct netflow *nf)
 }
 
 int
-netflow_set_collectors(struct netflow *nf, const struct svec *collectors_)
+netflow_set_options(struct netflow *nf,
+                    const struct netflow_options *nf_options)
 {
     struct svec collectors;
     int error = 0;
     size_t i;
+    long long int old_timeout;
+
+    nf->engine_type = nf_options->engine_type;
+    nf->engine_id = nf_options->engine_id;
+    nf->add_id_to_iface = nf_options->add_id_to_iface;
 
     clear_collectors(nf);
 
-    svec_clone(&collectors, collectors_);
+    svec_clone(&collectors, &nf_options->collectors);
     svec_sort_unique(&collectors);
 
     nf->fds = xmalloc(sizeof *nf->fds * collectors.n);
@@ -288,16 +311,19 @@ netflow_set_collectors(struct netflow *nf, const struct svec *collectors_)
     }
 
     svec_destroy(&collectors);
-    return error;
-}
 
-void 
-netflow_set_engine(struct netflow *nf, uint8_t engine_type, 
-        uint8_t engine_id, bool add_id_to_iface)
-{
-    nf->engine_type = engine_type;
-    nf->engine_id = engine_id;
-    nf->add_id_to_iface = add_id_to_iface;
+    old_timeout = nf->active_timeout;
+    if (nf_options->active_timeout != -1) {
+        nf->active_timeout = nf_options->active_timeout;
+    } else {
+        nf->active_timeout = ACTIVE_TIMEOUT_DEFAULT;
+    }
+    nf->active_timeout *= 1000;
+    if (old_timeout != nf->active_timeout) {
+        nf->reconfig_time = time_msec();
+    }
+
+    return error;
 }
 
 struct netflow *
@@ -324,3 +350,46 @@ netflow_destroy(struct netflow *nf)
         free(nf);
     }
 }
+
+void
+netflow_flow_clear(struct netflow_flow *nf_flow)
+{
+    uint16_t output_iface = nf_flow->output_iface;
+
+    memset(nf_flow, 0, sizeof *nf_flow);
+    nf_flow->output_iface = output_iface;
+}
+
+void
+netflow_flow_update_time(struct netflow *nf, struct netflow_flow *nf_flow,
+                         long long int used)
+{
+    if (!nf_flow->created) {
+        nf_flow->created = used;
+    }
+
+    if (!nf || !nf->active_timeout || !nf_flow->last_expired ||
+        nf->reconfig_time > nf_flow->last_expired) {
+        /* Keep the time updated to prevent a flood of expiration in
+         * the future. */
+        nf_flow->last_expired = time_msec();
+    }
+}
+
+void
+netflow_flow_update_flags(struct netflow_flow *nf_flow, uint8_t ip_tos,
+                          uint8_t tcp_flags)
+{
+    nf_flow->ip_tos = ip_tos;
+    nf_flow->tcp_flags |= tcp_flags;
+}
+
+bool
+netflow_active_timeout_expired(struct netflow *nf, struct netflow_flow *nf_flow)
+{
+    if (nf->active_timeout) {
+        return time_msec() > nf_flow->last_expired + nf->active_timeout;
+    }
+
+    return false;
+}
index 13be90b..cc7b960 100644 (file)
 #define NETFLOW_H 1
 
 #include "flow.h"
+#include "svec.h"
 
 struct ofexpired;
-struct svec;
+
+struct netflow_options {
+    struct svec collectors;
+    uint8_t engine_type;
+    uint8_t engine_id;
+    int active_timeout;
+    bool add_id_to_iface;
+};
+
+enum netflow_output_ports {
+    NF_OUT_FLOOD = UINT16_MAX,
+    NF_OUT_MULTI = UINT16_MAX - 1,
+    NF_OUT_DROP = UINT16_MAX - 2
+};
+
+struct netflow_flow {
+    long long int last_expired;   /* Time this flow last timed out. */
+    long long int created;        /* Time flow was created since time out. */
+
+    uint64_t packet_count_off;    /* Packet count at last time out. */
+    uint64_t byte_count_off;      /* Byte count at last time out. */
+
+    uint16_t output_iface;        /* Output interface index. */
+    uint8_t ip_tos;               /* Last-seen IP type-of-service. */
+    uint8_t tcp_flags;            /* Bitwise-OR of all TCP flags seen. */
+};
 
 struct netflow *netflow_create(void);
 void netflow_destroy(struct netflow *);
-int netflow_set_collectors(struct netflow *, const struct svec *collectors);
-void netflow_set_engine(struct netflow *nf, uint8_t engine_type, 
-        uint8_t engine_id, bool add_id_to_iface);
-void netflow_expire(struct netflow *, const struct ofexpired *);
+int netflow_set_options(struct netflow *, const struct netflow_options *);
+void netflow_expire(struct netflow *, struct netflow_flow *,
+                    struct ofexpired *);
 void netflow_run(struct netflow *);
 
+void netflow_flow_clear(struct netflow_flow *);
+void netflow_flow_update_time(struct netflow *, struct netflow_flow *,
+                              long long int used);
+void netflow_flow_update_flags(struct netflow_flow *, uint8_t ip_tos,
+                               uint8_t tcp_flags);
+bool netflow_active_timeout_expired(struct netflow *, struct netflow_flow *);
+
 #endif /* netflow.h */
index f31794f..4995bbe 100644 (file)
@@ -82,7 +82,7 @@ static int xlate_actions(const union ofp_action *in, size_t n_in,
                          const flow_t *flow, struct ofproto *ofproto,
                          const struct ofpbuf *packet,
                          struct odp_actions *out, tag_type *tags,
-                         bool *may_setup_flow);
+                         bool *may_set_up_flow, uint16_t *nf_output_iface);
 
 struct rule {
     struct cls_rule cr;
@@ -94,9 +94,8 @@ struct rule {
     uint64_t packet_count;      /* Number of packets received. */
     uint64_t byte_count;        /* Number of bytes received. */
     uint64_t accounted_bytes;   /* Number of bytes passed to account_cb. */
-    uint8_t tcp_flags;          /* Bitwise-OR of all TCP flags seen. */
-    uint8_t ip_tos;             /* Last-seen IP type-of-service. */
     tag_type tags;              /* Tags (set only by hooks). */
+    struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
 
     /* If 'super' is non-NULL, this rule is a subrule, that is, it is an
      * exact-match rule (having cr.wc.wildcards of 0) generated from the
@@ -145,9 +144,9 @@ rule_is_hidden(const struct rule *rule)
     return false;
 }
 
-static struct rule *rule_create(struct rule *super, const union ofp_action *,
-                                size_t n_actions, uint16_t idle_timeout,
-                                uint16_t hard_timeout);
+static struct rule *rule_create(struct ofproto *, struct rule *super,
+                                const union ofp_action *, size_t n_actions,
+                                uint16_t idle_timeout, uint16_t hard_timeout);
 static void rule_free(struct rule *);
 static void rule_destroy(struct ofproto *, struct rule *);
 static struct rule *rule_from_cls_rule(const struct cls_rule *);
@@ -242,8 +241,10 @@ static uint64_t pick_fallback_dpid(void);
 static void send_packet_in_miss(struct ofpbuf *, void *ofproto);
 static void send_packet_in_action(struct ofpbuf *, void *ofproto);
 static void update_used(struct ofproto *);
-static void update_stats(struct rule *, const struct odp_flow_stats *);
+static void update_stats(struct ofproto *, struct rule *,
+                         const struct odp_flow_stats *);
 static void expire_rule(struct cls_rule *, void *ofproto);
+static void active_timeout(struct ofproto *ofproto, struct rule *rule);
 static bool revalidate_rule(struct ofproto *p, struct rule *rule);
 static void revalidate_cb(struct cls_rule *rule_, void *p_);
 
@@ -532,16 +533,14 @@ ofproto_set_snoops(struct ofproto *ofproto, const struct svec *snoops)
 }
 
 int
-ofproto_set_netflow(struct ofproto *ofproto, const struct svec *collectors,
-        uint8_t engine_type, uint8_t engine_id, bool add_id_to_iface)
+ofproto_set_netflow(struct ofproto *ofproto,
+                    const struct netflow_options *nf_options)
 {
-    if (collectors && collectors->n) {
+    if (nf_options->collectors.n) {
         if (!ofproto->netflow) {
             ofproto->netflow = netflow_create();
         }
-        netflow_set_engine(ofproto->netflow, engine_type, engine_id, 
-                add_id_to_iface);
-        return netflow_set_collectors(ofproto->netflow, collectors);
+        return netflow_set_options(ofproto->netflow, nf_options);
     } else {
         netflow_destroy(ofproto->netflow);
         ofproto->netflow = NULL;
@@ -972,7 +971,7 @@ ofproto_send_packet(struct ofproto *p, const flow_t *flow,
     int error;
 
     error = xlate_actions(actions, n_actions, flow, p, packet, &odp_actions,
-                          NULL, NULL);
+                          NULL, NULL, NULL);
     if (error) {
         return error;
     }
@@ -991,7 +990,7 @@ ofproto_add_flow(struct ofproto *p,
                  int idle_timeout)
 {
     struct rule *rule;
-    rule = rule_create(NULL, actions, n_actions,
+    rule = rule_create(p, NULL, actions, n_actions,
                        idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 0);
     cls_rule_from_flow(&rule->cr, flow, wildcards, priority);
     rule_insert(p, rule, NULL, 0);
@@ -1385,7 +1384,7 @@ ofconn_wait(struct ofconn *ofconn)
 /* Caller is responsible for initializing the 'cr' member of the returned
  * rule. */
 static struct rule *
-rule_create(struct rule *super,
+rule_create(struct ofproto *ofproto, struct rule *super,
             const union ofp_action *actions, size_t n_actions,
             uint16_t idle_timeout, uint16_t hard_timeout)
 {
@@ -1401,6 +1400,9 @@ rule_create(struct rule *super,
     }
     rule->n_actions = n_actions;
     rule->actions = xmemdup(actions, n_actions * sizeof *actions);
+    netflow_flow_clear(&rule->nf_flow);
+    netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, rule->created);
+
     return rule;
 }
 
@@ -1489,7 +1491,7 @@ rule_execute(struct ofproto *ofproto, struct rule *rule,
     if (rule->cr.wc.wildcards || !flow_equal(flow, &rule->cr.flow)) {
         struct rule *super = rule->super ? rule->super : rule;
         if (xlate_actions(super->actions, super->n_actions, flow, ofproto,
-                          packet, &a, NULL, 0)) {
+                          packet, &a, NULL, 0, NULL)) {
             return;
         }
         actions = a.actions;
@@ -1504,8 +1506,9 @@ rule_execute(struct ofproto *ofproto, struct rule *rule,
                       actions, n_actions, packet)) {
         struct odp_flow_stats stats;
         flow_extract_stats(flow, packet, &stats);
-        update_stats(rule, &stats);
+        update_stats(ofproto, rule, &stats);
         rule->used = time_msec();
+        netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, rule->used);
     }
 }
 
@@ -1547,7 +1550,7 @@ static struct rule *
 rule_create_subrule(struct ofproto *ofproto, struct rule *rule,
                     const flow_t *flow)
 {
-    struct rule *subrule = rule_create(rule, NULL, 0,
+    struct rule *subrule = rule_create(ofproto, rule, NULL, 0,
                                        rule->idle_timeout, rule->hard_timeout);
     COVERAGE_INC(ofproto_subrule_create);
     cls_rule_from_flow(&subrule->cr, flow, 0,
@@ -1585,7 +1588,8 @@ rule_make_actions(struct ofproto *p, struct rule *rule,
     super = rule->super ? rule->super : rule;
     rule->tags = 0;
     xlate_actions(super->actions, super->n_actions, &rule->cr.flow, p,
-                  packet, &a, &rule->tags, &rule->may_install);
+                  packet, &a, &rule->tags, &rule->may_install,
+                  &rule->nf_flow.output_iface);
 
     actions_len = a.n_actions * sizeof *a.actions;
     if (rule->n_odp_actions != a.n_actions
@@ -1624,7 +1628,7 @@ rule_install(struct ofproto *p, struct rule *rule, struct rule *displaced_rule)
                          &put)) {
             rule->installed = true;
             if (displaced_rule) {
-                update_stats(rule, &put.flow.stats);
+                update_stats(p, displaced_rule, &put.flow.stats);
                 rule_post_uninstall(p, displaced_rule);
             }
         }
@@ -1648,14 +1652,27 @@ rule_reinstall(struct ofproto *ofproto, struct rule *rule)
 static void
 rule_update_actions(struct ofproto *ofproto, struct rule *rule)
 {
-    bool actions_changed = rule_make_actions(ofproto, rule, NULL);
+    bool actions_changed;
+    uint16_t new_out_iface, old_out_iface;
+
+    old_out_iface = rule->nf_flow.output_iface;
+    actions_changed = rule_make_actions(ofproto, rule, NULL);
+
     if (rule->may_install) {
         if (rule->installed) {
             if (actions_changed) {
-                /* XXX should really do rule_post_uninstall() for the *old* set
-                 * of actions, and distinguish the old stats from the new. */
                 struct odp_flow_put put;
-                do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY, &put);
+                do_put_flow(ofproto, rule, ODPPF_CREATE | ODPPF_MODIFY
+                                           | ODPPF_ZERO_STATS, &put);
+                update_stats(ofproto, rule, &put.flow.stats);
+
+                /* Temporarily set the old output iface so that NetFlow
+                 * messages have the correct output interface for the old
+                 * stats. */
+                new_out_iface = rule->nf_flow.output_iface;
+                rule->nf_flow.output_iface = old_out_iface;
+                rule_post_uninstall(ofproto, rule);
+                rule->nf_flow.output_iface = new_out_iface;
             }
         } else {
             rule_install(ofproto, rule, NULL);
@@ -1691,7 +1708,7 @@ rule_uninstall(struct ofproto *p, struct rule *rule)
         odp_flow.actions = NULL;
         odp_flow.n_actions = 0;
         if (!dpif_flow_del(p->dpif, &odp_flow)) {
-            update_stats(rule, &odp_flow.stats);
+            update_stats(p, rule, &odp_flow.stats);
         }
         rule->installed = false;
 
@@ -1699,39 +1716,51 @@ rule_uninstall(struct ofproto *p, struct rule *rule)
     }
 }
 
+static bool
+is_controller_rule(struct rule *rule)
+{
+    /* If the only action is send to the controller then don't report
+     * NetFlow expiration messages since it is just part of the control
+     * logic for the network and not real traffic. */
+
+    if (rule && rule->super) {
+        struct rule *super = rule->super;
+
+        return super->n_actions == 1 &&
+               super->actions[0].type == htons(OFPAT_OUTPUT) &&
+               super->actions[0].output.port == htons(OFPP_CONTROLLER);
+    }
+
+    return false;
+}
+
 static void
 rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
 {
     struct rule *super = rule->super;
 
     rule_account(ofproto, rule, 0);
-    if (ofproto->netflow && rule->byte_count) {
+
+    if (ofproto->netflow && !is_controller_rule(rule)) {
         struct ofexpired expired;
         expired.flow = rule->cr.flow;
         expired.packet_count = rule->packet_count;
         expired.byte_count = rule->byte_count;
         expired.used = rule->used;
-        expired.created = rule->created;
-        expired.tcp_flags = rule->tcp_flags;
-        expired.ip_tos = rule->ip_tos;
-        netflow_expire(ofproto->netflow, &expired);
+        netflow_expire(ofproto->netflow, &rule->nf_flow, &expired);
     }
     if (super) {
         super->packet_count += rule->packet_count;
         super->byte_count += rule->byte_count;
-        super->tcp_flags |= rule->tcp_flags;
-        if (rule->packet_count) {
-            super->ip_tos = rule->ip_tos;
-        }
-    }
 
-    /* Reset counters to prevent double counting if the rule ever gets
-     * reinstalled. */
-    rule->packet_count = 0;
-    rule->byte_count = 0;
-    rule->accounted_bytes = 0;
-    rule->tcp_flags = 0;
-    rule->ip_tos = 0;
+        /* Reset counters to prevent double counting if the rule ever gets
+         * reinstalled. */
+        rule->packet_count = 0;
+        rule->byte_count = 0;
+        rule->accounted_bytes = 0;
+
+        netflow_flow_clear(&rule->nf_flow);
+    }
 }
 \f
 static void
@@ -1897,9 +1926,14 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
 }
 
 static void
-add_output_group_action(struct odp_actions *actions, uint16_t group)
+add_output_group_action(struct odp_actions *actions, uint16_t group,
+                        uint16_t *nf_output_iface)
 {
     odp_actions_add(actions, ODPAT_OUTPUT_GROUP)->output_group.group = group;
+
+    if (group == DP_GROUP_ALL || group == DP_GROUP_FLOOD) {
+        *nf_output_iface = NF_OUT_FLOOD;
+    }
 }
 
 static void
@@ -1922,8 +1956,9 @@ struct action_xlate_ctx {
     /* Output. */
     struct odp_actions *out;    /* Datapath actions. */
     tag_type *tags;             /* Tags associated with OFPP_NORMAL actions. */
-    bool may_setup_flow;        /* True ordinarily; false if the actions must
+    bool may_set_up_flow;       /* True ordinarily; false if the actions must
                                  * be reassessed for every packet. */
+    uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
 };
 
 static void do_xlate_actions(const union ofp_action *in, size_t n_in,
@@ -1948,6 +1983,7 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
     }
 
     odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
+    ctx->nf_output_iface = port;
 }
 
 static struct rule *
@@ -1997,6 +2033,9 @@ xlate_output_action(struct action_xlate_ctx *ctx,
                     const struct ofp_action_output *oao)
 {
     uint16_t odp_port;
+    uint16_t prev_nf_output_iface = ctx->nf_output_iface;
+
+    ctx->nf_output_iface = NF_OUT_DROP;
 
     switch (ntohs(oao->port)) {
     case OFPP_IN_PORT:
@@ -2008,16 +2047,18 @@ xlate_output_action(struct action_xlate_ctx *ctx,
     case OFPP_NORMAL:
         if (!ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->packet,
                                               ctx->out, ctx->tags,
+                                              &ctx->nf_output_iface,
                                               ctx->ofproto->aux)) {
             COVERAGE_INC(ofproto_uninstallable);
-            ctx->may_setup_flow = false;
+            ctx->may_set_up_flow = false;
         }
         break;
     case OFPP_FLOOD:
-        add_output_group_action(ctx->out, DP_GROUP_FLOOD);
+        add_output_group_action(ctx->out, DP_GROUP_FLOOD,
+                                &ctx->nf_output_iface);
         break;
     case OFPP_ALL:
-        add_output_group_action(ctx->out, DP_GROUP_ALL);
+        add_output_group_action(ctx->out, DP_GROUP_ALL, &ctx->nf_output_iface);
         break;
     case OFPP_CONTROLLER:
         add_controller_action(ctx->out, oao);
@@ -2032,6 +2073,15 @@ xlate_output_action(struct action_xlate_ctx *ctx,
         }
         break;
     }
+
+    if (prev_nf_output_iface == NF_OUT_FLOOD) {
+        ctx->nf_output_iface = NF_OUT_FLOOD;
+    } else if (ctx->nf_output_iface == NF_OUT_DROP) {
+        ctx->nf_output_iface = prev_nf_output_iface;
+    } else if (prev_nf_output_iface != NF_OUT_DROP &&
+               ctx->nf_output_iface != NF_OUT_FLOOD) {
+        ctx->nf_output_iface = NF_OUT_MULTI;
+    }
 }
 
 static void
@@ -2110,11 +2160,21 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
             oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
             break;
 
+        case OFPAT_SET_NW_DST:
+            oa = odp_actions_add(ctx->out, ODPAT_SET_NW_DST);
+            oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
+            break;
+
         case OFPAT_SET_TP_SRC:
             oa = odp_actions_add(ctx->out, ODPAT_SET_TP_SRC);
             oa->tp_port.tp_port = ia->tp_port.tp_port;
             break;
 
+        case OFPAT_SET_TP_DST:
+            oa = odp_actions_add(ctx->out, ODPAT_SET_TP_DST);
+            oa->tp_port.tp_port = ia->tp_port.tp_port;
+            break;
+
         case OFPAT_VENDOR:
             xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
             break;
@@ -2130,7 +2190,8 @@ static int
 xlate_actions(const union ofp_action *in, size_t n_in,
               const flow_t *flow, struct ofproto *ofproto,
               const struct ofpbuf *packet,
-              struct odp_actions *out, tag_type *tags, bool *may_setup_flow)
+              struct odp_actions *out, tag_type *tags, bool *may_set_up_flow,
+              uint16_t *nf_output_iface)
 {
     tag_type no_tags = 0;
     struct action_xlate_ctx ctx;
@@ -2142,17 +2203,21 @@ xlate_actions(const union ofp_action *in, size_t n_in,
     ctx.packet = packet;
     ctx.out = out;
     ctx.tags = tags ? tags : &no_tags;
-    ctx.may_setup_flow = true;
+    ctx.may_set_up_flow = true;
+    ctx.nf_output_iface = NF_OUT_DROP;
     do_xlate_actions(in, n_in, &ctx);
 
-    /* Check with in-band control to see if we're allowed to setup this
+    /* Check with in-band control to see if we're allowed to set up this
      * flow. */
     if (!in_band_rule_check(ofproto->in_band, flow, out)) {
-        ctx.may_setup_flow = false;
+        ctx.may_set_up_flow = false;
     }
 
-    if (may_setup_flow) {
-        *may_setup_flow = ctx.may_setup_flow;
+    if (may_set_up_flow) {
+        *may_set_up_flow = ctx.may_set_up_flow;
+    }
+    if (nf_output_iface) {
+        *nf_output_iface = ctx.nf_output_iface;
     }
     if (odp_actions_overflow(out)) {
         odp_actions_init(out);
@@ -2193,7 +2258,7 @@ handle_packet_out(struct ofproto *p, struct ofconn *ofconn,
 
     flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
     error = xlate_actions((const union ofp_action *) opo->actions, n_actions,
-                          &flow, p, &payload, &actions, NULL, NULL);
+                          &flow, p, &payload, &actions, NULL, NULL, NULL);
     if (error) {
         return error;
     }
@@ -2423,12 +2488,17 @@ query_stats(struct ofproto *p, struct rule *rule,
     struct odp_flow *odp_flows;
     size_t n_odp_flows;
 
+    packet_count = rule->packet_count;
+    byte_count = rule->byte_count;
+
     n_odp_flows = rule->cr.wc.wildcards ? list_size(&rule->list) : 1;
     odp_flows = xcalloc(1, n_odp_flows * sizeof *odp_flows);
     if (rule->cr.wc.wildcards) {
         size_t i = 0;
         LIST_FOR_EACH (subrule, struct rule, list, &rule->list) {
             odp_flows[i++].key = subrule->cr.flow;
+            packet_count += subrule->packet_count;
+            byte_count += subrule->byte_count;
         }
     } else {
         odp_flows[0].key = rule->cr.flow;
@@ -2680,23 +2750,29 @@ msec_from_nsec(uint64_t sec, uint32_t nsec)
 }
 
 static void
-update_time(struct rule *rule, const struct odp_flow_stats *stats)
+update_time(struct ofproto *ofproto, struct rule *rule,
+            const struct odp_flow_stats *stats)
 {
     long long int used = msec_from_nsec(stats->used_sec, stats->used_nsec);
     if (used > rule->used) {
         rule->used = used;
+        if (rule->super && used > rule->super->used) {
+            rule->super->used = used;
+        }
+        netflow_flow_update_time(ofproto->netflow, &rule->nf_flow, used);
     }
 }
 
 static void
-update_stats(struct rule *rule, const struct odp_flow_stats *stats)
+update_stats(struct ofproto *ofproto, struct rule *rule,
+             const struct odp_flow_stats *stats)
 {
-    update_time(rule, stats);
-    rule->packet_count += stats->n_packets;
-    rule->byte_count += stats->n_bytes;
-    rule->tcp_flags |= stats->tcp_flags;
     if (stats->n_packets) {
-        rule->ip_tos = stats->ip_tos;
+        update_time(ofproto, rule, stats);
+        rule->packet_count += stats->n_packets;
+        rule->byte_count += stats->n_bytes;
+        netflow_flow_update_flags(&rule->nf_flow, stats->ip_tos,
+                                  stats->tcp_flags);
     }
 }
 
@@ -2709,7 +2785,7 @@ add_flow(struct ofproto *p, struct ofconn *ofconn,
     uint16_t in_port;
     int error;
 
-    rule = rule_create(NULL, (const union ofp_action *) ofm->actions,
+    rule = rule_create(p, NULL, (const union ofp_action *) ofm->actions,
                        n_actions, ntohs(ofm->idle_timeout),
                        ntohs(ofm->hard_timeout));
     cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
@@ -2900,7 +2976,7 @@ handle_ofmp(struct ofproto *p, struct ofconn *ofconn,
 {
     size_t msg_len = ntohs(ofmph->header.header.length);
     if (msg_len < sizeof(*ofmph)) {
-        VLOG_WARN_RL(&rl, "dropping short managment message: %d\n", msg_len);
+        VLOG_WARN_RL(&rl, "dropping short managment message: %zu\n", msg_len);
         return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
     }
 
@@ -2908,7 +2984,7 @@ handle_ofmp(struct ofproto *p, struct ofconn *ofconn,
         struct ofmp_capability_request *ofmpcr;
 
         if (msg_len < sizeof(struct ofmp_capability_request)) {
-            VLOG_WARN_RL(&rl, "dropping short capability request: %d\n", 
+            VLOG_WARN_RL(&rl, "dropping short capability request: %zu\n",
                     msg_len);
             return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
         }
@@ -3169,9 +3245,9 @@ compose_flow_exp(const struct rule *rule, long long int now, uint8_t reason)
     flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofe->match);
     ofe->priority = htons(rule->cr.priority);
     ofe->reason = reason;
-    ofe->duration = (now - rule->created) / 1000;
-    ofe->packet_count = rule->packet_count;
-    ofe->byte_count = rule->byte_count;
+    ofe->duration = htonl((now - rule->created) / 1000);
+    ofe->packet_count = htonll(rule->packet_count);
+    ofe->byte_count = htonll(rule->byte_count);
 
     return buf;
 }
@@ -3234,38 +3310,75 @@ expire_rule(struct cls_rule *cls_rule, void *p_)
                    ? rule->used + rule->idle_timeout * 1000
                    : LLONG_MAX);
     expire = MIN(hard_expire, idle_expire);
-    if (expire == LLONG_MAX) {
-        if (rule->installed && time_msec() >= rule->used + 5000) {
-            uninstall_idle_flow(p, rule);
-        }
-        return;
-    }
 
     now = time_msec();
     if (now < expire) {
         if (rule->installed && now >= rule->used + 5000) {
             uninstall_idle_flow(p, rule);
+        } else if (!rule->cr.wc.wildcards) {
+            active_timeout(p, rule);
         }
+
         return;
     }
 
     COVERAGE_INC(ofproto_expired);
+
+    /* Update stats.  This code will be a no-op if the rule expired
+     * due to an idle timeout. */
     if (rule->cr.wc.wildcards) {
-        /* Update stats.  (This code will be a no-op if the rule expired
-         * due to an idle timeout, because in that case the rule has no
-         * subrules left.) */
         struct rule *subrule, *next;
         LIST_FOR_EACH_SAFE (subrule, next, struct rule, list, &rule->list) {
             rule_remove(p, subrule);
         }
+    } else {
+        rule_uninstall(p, rule);
     }
 
-    send_flow_exp(p, rule, now,
-                  (now >= hard_expire
-                   ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
+    if (!rule_is_hidden(rule)) {
+        send_flow_exp(p, rule, now,
+                      (now >= hard_expire
+                       ? OFPER_HARD_TIMEOUT : OFPER_IDLE_TIMEOUT));
+    }
     rule_remove(p, rule);
 }
 
+static void
+active_timeout(struct ofproto *ofproto, struct rule *rule)
+{
+    if (ofproto->netflow && !is_controller_rule(rule) &&
+        netflow_active_timeout_expired(ofproto->netflow, &rule->nf_flow)) {
+        struct ofexpired expired;
+        struct odp_flow odp_flow;
+
+        /* Get updated flow stats. */
+        memset(&odp_flow, 0, sizeof odp_flow);
+        if (rule->installed) {
+            odp_flow.key = rule->cr.flow;
+            odp_flow.flags = ODPFF_ZERO_TCP_FLAGS;
+            dpif_flow_get(ofproto->dpif, &odp_flow);
+
+            if (odp_flow.stats.n_packets) {
+                update_time(ofproto, rule, &odp_flow.stats);
+                netflow_flow_update_flags(&rule->nf_flow, odp_flow.stats.ip_tos,
+                                          odp_flow.stats.tcp_flags);
+            }
+        }
+
+        expired.flow = rule->cr.flow;
+        expired.packet_count = rule->packet_count +
+                               odp_flow.stats.n_packets;
+        expired.byte_count = rule->byte_count + odp_flow.stats.n_bytes;
+        expired.used = rule->used;
+
+        netflow_expire(ofproto->netflow, &rule->nf_flow, &expired);
+
+        /* Schedule us to send the accumulated records once we have
+         * collected all of them. */
+        poll_immediate_wake();
+    }
+}
+
 static void
 update_used(struct ofproto *p)
 {
@@ -3291,7 +3404,7 @@ update_used(struct ofproto *p)
             continue;
         }
 
-        update_time(rule, &f->stats);
+        update_time(p, rule, &f->stats);
         rule_account(p, rule, f->stats.n_bytes);
     }
     free(flows);
@@ -3395,7 +3508,7 @@ pick_fallback_dpid(void)
 static bool
 default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
                          struct odp_actions *actions, tag_type *tags,
-                         void *ofproto_)
+                         uint16_t *nf_output_iface, void *ofproto_)
 {
     struct ofproto *ofproto = ofproto_;
     int out_port;
@@ -3422,9 +3535,10 @@ default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
     /* Determine output port. */
     out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags);
     if (out_port < 0) {
-        add_output_group_action(actions, DP_GROUP_FLOOD);
+        add_output_group_action(actions, DP_GROUP_FLOOD, nf_output_iface);
     } else if (out_port != flow->in_port) {
         odp_actions_add(actions, ODPAT_OUTPUT)->output.port = out_port;
+        *nf_output_iface = out_port;
     } else {
         /* Drop. */
     }
index 398cac4..50dd5d5 100644 (file)
@@ -21,6 +21,7 @@
 #include <stddef.h>
 #include <stdint.h>
 #include "flow.h"
+#include "netflow.h"
 #include "tag.h"
 
 struct odp_actions;
@@ -30,12 +31,9 @@ struct svec;
 
 struct ofexpired {
     flow_t flow;
-    uint64_t packet_count;      /* Packets from *expired* subrules. */
-    uint64_t byte_count;        /* Bytes from *expired* subrules. */
+    uint64_t packet_count;      /* Packets from subrules. */
+    uint64_t byte_count;        /* Bytes from subrules. */
     long long int used;         /* Last-used time (0 if never used). */
-    long long int created;      /* Creation time. */
-    uint8_t tcp_flags;          /* Bitwise-OR of all TCP flags seen. */
-    uint8_t ip_tos;             /* Last-seen IP type-of-service. */
 };
 
 int ofproto_create(const char *datapath, const struct ofhooks *, void *aux,
@@ -62,8 +60,8 @@ int ofproto_set_discovery(struct ofproto *, bool discovery,
 int ofproto_set_controller(struct ofproto *, const char *controller);
 int ofproto_set_listeners(struct ofproto *, const struct svec *listeners);
 int ofproto_set_snoops(struct ofproto *, const struct svec *snoops);
-int ofproto_set_netflow(struct ofproto *, const struct svec *collectors,
-        uint8_t engine_type, uint8_t engine_id, bool add_id_to_iface);
+int ofproto_set_netflow(struct ofproto *,
+                        const struct netflow_options *nf_options);
 void ofproto_set_failure(struct ofproto *, bool fail_open);
 void ofproto_set_rate_limit(struct ofproto *, int rate_limit, int burst_limit);
 int ofproto_set_stp(struct ofproto *, bool enable_stp);
@@ -99,7 +97,8 @@ struct ofhooks {
     void (*port_changed_cb)(enum ofp_port_reason, const struct ofp_phy_port *,
                             void *aux);
     bool (*normal_cb)(const flow_t *, const struct ofpbuf *packet,
-                      struct odp_actions *, tag_type *, void *aux);
+                      struct odp_actions *, tag_type *,
+                      uint16_t *nf_output_iface, void *aux);
     void (*account_flow_cb)(const flow_t *, const union odp_action *,
                             size_t n_actions, unsigned long long int n_bytes,
                             void *aux);
index dcf6d20..85812c4 100644 (file)
@@ -7,6 +7,15 @@ m4_define([RUN_OVS_VSCTL],
 m4_foreach([command], [$@], [ovs-vsctl --no-reload --config=conf command
 ])])
 
+dnl RUN_OVS_VSCTL_TOGETHER(COMMAND, ...)
+dnl
+dnl Executes each ovs-vsctl COMMAND on a file named "conf" in the
+dnl current directory, in a single run of ovs-vsctl.  Creates "conf" if it
+dnl does not already exist.
+m4_define([RUN_OVS_VSCTL_TOGETHER],
+  [: >> conf
+   ovs-vsctl --no-reload --config=conf m4_join([ -- ], $@)])
+
 dnl CHECK_BRIDGES([BRIDGE, PARENT, VLAN], ...)
 dnl
 dnl Verifies that "ovs-vsctl list-br" prints the specified list of bridges,
@@ -15,16 +24,37 @@ dnl specified PARENT and is on the given VLAN.
 m4_define([_CHECK_BRIDGE],
   [AT_CHECK([RUN_OVS_VSCTL([br-to-parent $1])], [0], [$2
 ])
+
+   # Check br-to-vlan, without --oneline.
    AT_CHECK([RUN_OVS_VSCTL([br-to-vlan $1])], [0], [$3
+])
+   # Check br-to-vlan, with --oneline.
+   # (This particular test is interesting with --oneline because it returns
+   # an integer instead of a string and that can cause type mismatches inside
+   # python if not done carefully.)
+   AT_CHECK([RUN_OVS_VSCTL([--oneline br-to-vlan $1])], [0], [$3
+])
+
+   # Check multiple queries in a single run.
+   AT_CHECK([RUN_OVS_VSCTL_TOGETHER([br-to-parent $1], [br-to-vlan $1])], [0],
+[$2
+$3
 ])])
 m4_define([CHECK_BRIDGES],
-  [dnl Check that the bridges appear on list-br.
+  [dnl Check that the bridges appear on list-br, without --oneline.
    AT_CHECK(
      [RUN_OVS_VSCTL([list-br])],
      [0],
      [m4_foreach([brinfo], [$@], [m4_car(brinfo)
 ])])
 
+   dnl Check that the bridges appear on list-br, with --oneline.
+   AT_CHECK(
+     [RUN_OVS_VSCTL([--oneline list-br])],
+     [0],
+     [m4_join([\n], m4_foreach([brinfo], [$@], [m4_car(brinfo),]))
+])
+
    dnl Check that each bridge exists according to br-exists and that
    dnl a bridge that should not exist does not.
    m4_foreach([brinfo], [$@], 
@@ -41,11 +71,19 @@ dnl list of ports, which must be in alphabetical order.  Also checks
 dnl that "ovs-vsctl port-to-br" reports that each port is
 dnl in BRIDGE.
 m4_define([CHECK_PORTS],
-  [AT_CHECK(
+  [dnl Check ports without --oneline.
+   AT_CHECK(
      [RUN_OVS_VSCTL([list-ports $1])],
      [0],
      [m4_foreach([port], m4_cdr($@), [port
 ])])
+
+   dnl Check ports with --oneline.
+   AT_CHECK(
+     [RUN_OVS_VSCTL([--oneline list-ports $1])],
+     [0],
+     [m4_join([\n], m4_shift($@))
+])
    AT_CHECK([RUN_OVS_VSCTL([port-to-br $1])], [1], [], [ovs-vsctl: no port named $1
 ])
    m4_foreach(
@@ -86,6 +124,14 @@ CHECK_PORTS([a])
 CHECK_IFACES([a])
 AT_CLEANUP
 
+AT_SETUP([add-br a, add-br a])
+AT_KEYWORDS([ovs-vsctl])
+AT_CHECK([RUN_OVS_VSCTL([add-br a])])
+AT_CHECK([RUN_OVS_VSCTL([add-br a])], [1], [],
+  [ovs-vsctl: cannot create a bridge named a because a bridge named a already exists
+])
+AT_CLEANUP
+
 AT_SETUP([add-br a, add-br b])
 AT_KEYWORDS([ovs-vsctl])
 AT_CHECK([RUN_OVS_VSCTL([add-br a], [add-br b])])
@@ -111,9 +157,39 @@ CHECK_PORTS([b])
 CHECK_IFACES([b])
 AT_CLEANUP
 
-AT_SETUP([add-br a b, add-port a a1, add-port b b1, del-br a])
+AT_SETUP([add-br a, add-port a a1, add-port a a2])
+AT_KEYWORDS([ovs-vsctl])
+AT_CHECK([RUN_OVS_VSCTL(
+   [add-br a], 
+   [add-port a a1],
+   [add-port a a2])])
+AT_CHECK([cat conf], [0],
+  [bridge.a.port=a
+bridge.a.port=a1
+bridge.a.port=a2
+])
+CHECK_BRIDGES([a, a, 0])
+CHECK_PORTS([a], [a1], [a2])
+CHECK_IFACES([a], [a1], [a2])
+AT_CLEANUP
+
+AT_SETUP([add-br a, add-port a a1, add-port a a1])
 AT_KEYWORDS([ovs-vsctl])
 AT_CHECK([RUN_OVS_VSCTL(
+   [add-br a], 
+   [add-port a a1])])
+AT_CHECK([cat conf], [0],
+  [bridge.a.port=a
+bridge.a.port=a1
+])
+AT_CHECK([RUN_OVS_VSCTL([add-port a a1])], [1], [],
+  [ovs-vsctl: cannot create a port named a1 because a port named a1 already exists on bridge a
+])
+AT_CLEANUP
+
+AT_SETUP([add-br a b, add-port a a1, add-port b b1, del-br a])
+AT_KEYWORDS([ovs-vsctl])
+AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
    [add-br a], 
    [add-br b], 
    [add-port a a1],
@@ -165,12 +241,12 @@ CHECK_PORTS([b], [b1])
 CHECK_IFACES([b], [b1])
 AT_CLEANUP
 
-AT_SETUP([add-br a, add-bond a bond0 a1 a2 a3, del-port bond0])
+AT_SETUP([add-br a, add-bond a bond0 a1 a2 a3, del-port bond0])
 AT_KEYWORDS([ovs-vsctl])
-AT_CHECK([RUN_OVS_VSCTL(
+AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
   [add-br a], 
   [add-bond a bond0 a1 a2 a3],
-  [del-port bond0])])
+  [del-port bond0])])
 AT_CHECK([cat conf], [0], [dnl
 bridge.a.port=a
 ])
@@ -259,7 +335,8 @@ AT_CLEANUP
 AT_SETUP([fake bridge on bond + del-br fake bridge])
 AT_KEYWORDS([ovs-vsctl fake-bridge])
 AT_DATA([conf], [BOND_FAKE_CONF])
-AT_CHECK([RUN_OVS_VSCTL([del-br xapi2])])
+AT_CHECK([RUN_OVS_VSCTL([--oneline del-br xapi2])], [0], [
+])
 CHECK_BRIDGES([xapi1, xapi1, 0])
 CHECK_PORTS([xapi1], [bond0])
 CHECK_IFACES([xapi1], [eth0], [eth1])
index 61ce4d4..4ad20f2 100644 (file)
@@ -4,58 +4,65 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-appctl 8 "April 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-appctl 8 "November 2009" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovs\-appctl
 
 .SH NAME
 ovs\-appctl \- utility for configuring running Open vSwitch daemons
 
 .SH SYNOPSIS
-\fBovs\-appctl\fR [\fB-h\fR | \fB--help\fR] [\fItarget\fR...] [\fIaction\fR...]
-.sp 1
-The available \fItarget\fR options are:
+\fBovs\-appctl\fR [\fB--target=\fItarget\fR | \fB-t\fR \fItarget\fR]
+\fIcommand \fR[\fIarg\fR...]
 .br
-[\fB-t\fR \fIsocket\fR | \fB--target=\fIsocket\fR]
-.sp 1
-The available \fIaction\fR options are:
+\fBovs\-appctl\fR --help
 .br
-[\fB-l\fR | \fB--list\fR] [\fB-s\fR
-\fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]] |
-\fB--set=\fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]]]
-[\fB-r\fR | \fB--reopen\fR]
-[\fB-e\fR | \fB--execute=\fIcommand\fR]
-
+\fBovs\-appctl\fR --version
 .SH DESCRIPTION
-The \fBovs\-appctl\fR program connects to one or more running
-Open vSwitch daemons (such as \fBovs\-vswitchd\fR(8)), as specified by the
-user, and sends them commands to query or modify their behavior.
-Its primary purpose is currently to adjust daemons' logging levels.
-
-\fBovs\-appctl\fR applies one or more actions to each of one or more
-target processes.  Targets may be specified using:
-
-.IP "\fB-t \fIsocket\fR"
-.IQ "\fB--target=\fIsocket\fR"
-The specified \fIsocket\fR must be the name of a Unix domain socket
-for a \fBovs\-appctl\fR-controllable process.  If \fIsocket\fR does not
-begin with \fB/\fR, it is treated as relative to \fB@RUNDIR@\fR.
-
-Each Open vSwitch daemon by default creates a socket named
-\fB@RUNDIR@/\fIprogram\fB.\fIpid\fB.ctl\fR, where \fIprogram\fR is
-the program's name (such as \fBovs\-vswitchd\fR) and \fIpid\fR is the
-daemon's PID.
-
-.PP
-The available actions are:
-
-.IP "\fB-l\fR"
-.IQ "\fB--list\fR"
-Print the list of known logging modules and their current levels to
-stdout.
-
-.IP "\fB-s\fR \fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]]"
-.IQ "\fB--set=\fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]]"
-
+Open vSwitch daemons accept certain commands at runtime to control
+their behavior and query their settings.  Every daemon accepts the
+commands for querying and adjusting its logging settings documented
+under \fBLOGGING COMMANDS\fR below, and \fBovs\-vswitchd\fR in
+particular accepts a number of additional commands documented in
+\fBovs\-vswitchd\fR(8).
+
+The \fBovs\-appctl\fR program provides a simple way to invoke these
+commands.  The command to be sent is specified on \fBovs\-appctl\fR's
+command line as non-option arguments.  \fBovs\-appctl\fR sends the
+command and prints the daemon's response on standard output.
+
+In normal use only a single option is accepted:
+
+.IP "\fB\-t \fItarget\fR"
+.IQ "\fB\-\-target=\fItarget\fR"
+Tells \fBovs\-appctl\fR which daemon to contact.
+.IP
+If \fItarget\fR begins with \fB/\fR it must name a Unix domain socket
+on which an Open vSwitch daemon is listening for control channel
+connections.  By default, each daemon listens on a Unix domain socket
+named \fB@RUNDIR@/\fIprogram\fB.\fIpid\fB.ctl\fR, where \fIprogram\fR
+is the program's name and \fIpid\fR is its process ID.  For example,
+if \fBovs-vswitchd\fR has PID 123, it would listen on
+\fB@RUNDIR@/ovs-vswitchd.123.ctl\fR.
+.IP
+Otherwise, \fBovs\-appctl\fR looks for a pidfile, that is, a file
+whose contents are the process ID of a running process as a decimal
+number, named \fB@RUNDIR@/\fItarget\fB.pid\fR.  (The \fB\-\-pidfile\fR
+option makes an Open vSwitch daemon create a pidfile.)
+\fBovs\-appctl\fR reads the pidfile, then looks for a Unix socket
+named \fB@RUNDIR@/\fItarget\fB.\fIpid\fB.ctl\fR, where \fIpid\fR is
+replaced by the process ID read from the pidfile, and uses that file
+as if it had been specified directly as the target.
+.IP
+The default target is \fBovs\-vswitchd\fR.
+.
+.SH LOGGING COMMANDS
+Every Open vSwitch daemon supports the following commands for
+examining and adjusting log levels.
+.
+.IP "\fBvlog/list\fR"
+Lists the known logging modules and their current levels.
+.
+.IP "\fBvlog/set\fR \fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]]"
 Sets the logging level for \fImodule\fR in \fIfacility\fR to
 \fIlevel\fR.  The \fImodule\fR may be any valid module name (as
 displayed by the \fB--list\fR option) or the special name \fBANY\fR to
@@ -67,10 +74,8 @@ logging levels for both facilities.  If it is omitted,
 \fBemer\fR, \fBerr\fR, \fBwarn\fR, \fBinfo\fR, or \fBdbg\fR, designating the
 minimum severity of a message for it to be logged.  If it is omitted,
 \fIlevel\fR defaults to \fBdbg\fR.
-
-.IP "\fB-s PATTERN:\fIfacility\fB:\fIpattern\fR"
-.IQ "\fB--set=PATTERN:\fIfacility\fB:\fIpattern\fR"
-
+.
+.IP "\fBvlog/set PATTERN:\fIfacility\fB:\fIpattern\fR"
 Sets the log pattern for \fIfacility\fR to \fIpattern\fR.  Each time a
 message is logged to \fIfacility\fR, \fIpattern\fR determines the
 message's formatting.  Most characters in \fIpattern\fR are copied
@@ -140,27 +145,29 @@ width.  (A field wider than \fIwidth\fR is not truncated to fit.)
 The default pattern for console output is \fB%d{%b %d
 %H:%M:%S}|%05N|%c|%p|%m\fR; for syslog output, \fB%05N|%c|%p|%m\fR.
 
-.IP \fB-r\fR
-.IQ \fB--reopen\fR
-Causes the target application to close and reopen its log file.  (This
+.IP "\fBvlog/reopen\fR"
+Causes the daemon to close and reopen its log file.  (This
 is useful after rotating log files, to cause a new log file to be
 used.)
 
 This has no effect if the target application was not invoked with the
 \fB--log-file\fR option.
 
-.IP "\fB-e \fIcommand\fR"
-.IQ "\fB--execute=\fIcommand\fR"
-Passes the specified \fIcommand\fR literally to the target application
-and prints its response to stdout, if successful, or to stderr if an
-error occurs.  Use \fB-e help\fR to print a list of available commands.
-
 .SH OPTIONS
 
 .so lib/common.man
 
+.SH BUGS
+
+The protocol used to speak to Open vSwitch daemons does not contain a
+quoting mechanism, so command arguments should not generally contain
+white space.
+
 .SH "SEE ALSO"
 
+\fBovs\-appctl\fR can control the following daemons:
+.BR ovs\-vswitchd (8),
+.BR ovs\-openflowd (8),
 .BR ovs\-controller (8),
-.BR ovs\-dpctl (8),
-.BR ovs\-openflowd (8)
+.BR ovs\-brcompatd (8),
+.BR ovs\-discover (8).
index 9349662..1302bf2 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include <config.h>
-#include "vlog.h"
 
-#include <dirent.h>
 #include <errno.h>
 #include <getopt.h>
-#include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 
 #include "command-line.h"
-#include "compiler.h"
+#include "daemon.h"
+#include "dirs.h"
+#include "dynamic-string.h"
 #include "timeval.h"
 #include "unixctl.h"
 #include "util.h"
 
-static void
-usage(char *prog_name, int exit_code)
-{
-    printf("Usage: %s [TARGET] [ACTION...]\n"
-           "Targets:\n"
-           "  -t, --target=TARGET  Path to Unix domain socket\n"
-           "Actions:\n"
-           "  -l, --list         List current settings\n"
-           "  -s, --set=MODULE[:FACILITY[:LEVEL]]\n"
-           "        Set MODULE and FACILITY log level to LEVEL\n"
-           "        MODULE may be any valid module name or 'ANY'\n"
-           "        FACILITY may be 'syslog', 'console', 'file', or 'ANY' (default)\n"
-           "        LEVEL may be 'emer', 'err', 'warn', 'info', or 'dbg' (default)\n"
-           "  -r, --reopen       Make the program reopen its log file\n"
-           "  -e, --execute=COMMAND  Execute control COMMAND and print its output\n"
-           "Other options:\n"
-           "  -h, --help         Print this helpful information\n"
-           "  -V, --version      Display version information\n",
-           prog_name);
-    exit(exit_code);
-}
+static void usage(void);
+static const char *parse_command_line(int argc, char *argv[]);
+static struct unixctl_client *connect_to_target(const char *target);
 
-static char *
-transact(struct unixctl_client *client, const char *request, bool *ok)
+int
+main(int argc, char *argv[])
 {
-    int code;
+    struct unixctl_client *client;
+    const char *target;
+    struct ds request;
+    int code, error;
     char *reply;
-    int error = unixctl_client_transact(client, request, &code, &reply);
-    if (error) {
-        fprintf(stderr, "%s: transaction error: %s\n",
-                unixctl_client_target(client), strerror(error));
-        *ok = false;
-        return xstrdup("");
-    } else {
-        if (code / 100 != 2) {
-            fprintf(stderr, "%s: server returned reply code %03d\n",
-                    unixctl_client_target(client), code);
+    int i;
+
+    set_program_name(argv[0]);
+    time_init();
+
+    /* Parse command line and connect to target. */
+    target = parse_command_line(argc, argv);
+    client = connect_to_target(target);
+
+    /* Compose request. */
+    ds_init(&request);
+    for (i = optind; i < argc; i++) {
+        if (i != optind) {
+            ds_put_char(&request, ' ');
         }
-        return reply;
+        ds_put_cstr(&request, argv[i]);
     }
-}
-
-static void
-transact_ack(struct unixctl_client *client, const char *request, bool *ok)
-{
-    free(transact(client, request, ok));
-}
 
-static void
-execute_command(struct unixctl_client *client, const char *request, bool *ok)
-{
-    int code;
-    char *reply;
-    int error = unixctl_client_transact(client, request, &code, &reply);
+    /* Transact request and process reply. */
+    error = unixctl_client_transact(client, ds_cstr(&request), &code, &reply);
     if (error) {
-        fprintf(stderr, "%s: transaction error: %s\n",
-                unixctl_client_target(client), strerror(error));
-        *ok = false;
-    } else {
-        if (code / 100 != 2) {
-            fprintf(stderr, "%s: server returned reply code %03d\n",
-                    unixctl_client_target(client), code);
-            fputs(reply, stderr);
-            *ok = false;
-        } else {
-            fputs(reply, stdout);
-        }
+        ovs_fatal(error, "%s: transaction error", target);
+    }
+    if (code / 100 != 2) {
+        ovs_error(0, "%s: server returned reply code %03d", target, code);
+        exit(2);
     }
+    fputs(reply, stdout);
+
+    return 0;
 }
 
 static void
-add_target(struct unixctl_client ***clients, size_t *n_clients,
-           const char *path, bool *ok)
+usage(void)
 {
-    struct unixctl_client *client;
-    int error = unixctl_client_create(path, &client);
-    if (error) {
-        fprintf(stderr, "Error connecting to \"%s\": %s\n",
-                path, strerror(error));
-        *ok = false;
-    } else {
-        *clients = xrealloc(*clients, sizeof *clients * (*n_clients + 1));
-        (*clients)[*n_clients] = client;
-        ++*n_clients;
-    }
+    printf("%s, for querying and controlling Open vSwitch daemon\n"
+           "usage: %s [TARGET] COMMAND [ARG...]\n"
+           "Targets:\n"
+           "  -t, --target=TARGET  pidfile or socket to contact\n"
+           "Common commands:"
+           "  help               List commands supported by the target\n"
+           "  vlog/list          List current logging levels\n"
+           "  vlog/set MODULE[:FACILITY[:LEVEL]]\n"
+           "        Set MODULE and FACILITY log level to LEVEL\n"
+           "        MODULE may be any valid module name or 'ANY'\n"
+           "        FACILITY may be 'syslog', 'console', 'file', or 'ANY' (default)\n"
+           "        LEVEL may be 'emer', 'err', 'warn', 'info', or 'dbg' (default)\n"
+           "  vlog/reopen        Make the program reopen its log file\n"
+           "Other options:\n"
+           "  -h, --help         Print this helpful information\n"
+           "  -V, --version      Display version information\n",
+           program_name, program_name);
+    exit(EXIT_SUCCESS);
 }
 
-int main(int argc, char *argv[])
+static const char *
+parse_command_line(int argc, char *argv[])
 {
     static const struct option long_options[] = {
-        /* Target options must come first. */
         {"target", required_argument, NULL, 't'},
+        {"execute", no_argument, NULL, 'e'},
         {"help", no_argument, NULL, 'h'},
         {"version", no_argument, NULL, 'V'},
-
-        /* Action options come afterward. */
-        {"list", no_argument, NULL, 'l'},
-        {"set", required_argument, NULL, 's'},
-        {"reopen", no_argument, NULL, 'r'},
-        {"execute", required_argument, NULL, 'e'},
         {0, 0, 0, 0},
     };
-    char *short_options;
-
-    /* Determine targets. */
-    bool ok = true;
-    int n_actions = 0;
-    struct unixctl_client **clients = NULL;
-    size_t n_clients = 0;
+    const char *target;
+    int e_options;
 
-    set_program_name(argv[0]);
-    time_init();
-
-    short_options = long_options_to_short_options(long_options);
+    target = NULL;
+    e_options = 0;
     for (;;) {
         int option;
-        size_t i;
 
-        option = getopt_long(argc, argv, short_options, long_options, NULL);
+        option = getopt_long(argc, argv, "+t:hVe", long_options, NULL);
         if (option == -1) {
             break;
         }
-        if (!strchr("thV", option) && n_clients == 0) {
-            ovs_fatal(0, "no targets specified (use --help for help)");
-        } else {
-            ++n_actions;
-        }
         switch (option) {
         case 't':
-            add_target(&clients, &n_clients, optarg, &ok);
-            break;
-
-        case 'l':
-            for (i = 0; i < n_clients; i++) {
-                struct unixctl_client *client = clients[i];
-                char *reply;
-
-                printf("%s:\n", unixctl_client_target(client));
-                reply = transact(client, "vlog/list", &ok);
-                fputs(reply, stdout);
-                free(reply);
-            }
-            break;
-
-        case 's':
-            for (i = 0; i < n_clients; i++) {
-                struct unixctl_client *client = clients[i];
-                char *request = xasprintf("vlog/set %s", optarg);
-                transact_ack(client, request, &ok);
-                free(request);
-            }
-            break;
-
-        case 'r':
-            for (i = 0; i < n_clients; i++) {
-                struct unixctl_client *client = clients[i];
-                char *request = xstrdup("vlog/reopen");
-                transact_ack(client, request, &ok);
-                free(request);
+            if (target) {
+                ovs_fatal(0, "-t or --target may be specified only once");
             }
+            target = optarg;
             break;
 
         case 'e':
-            for (i = 0; i < n_clients; i++) {
-                execute_command(clients[i], optarg, &ok);
+            /* We ignore -e for compatibility.  Older versions specified the
+             * command as the argument to -e.  Since the current version takes
+             * the command as non-option arguments and we say that -e has no
+             * arguments, this just works in the common case. */
+            if (e_options++) {
+                ovs_fatal(0, "-e or --execute may be speciifed only once");
             }
             break;
 
         case 'h':
-            usage(argv[0], EXIT_SUCCESS);
+            usage();
             break;
 
         case 'V':
@@ -213,9 +152,44 @@ int main(int argc, char *argv[])
             NOT_REACHED();
         }
     }
-    if (!n_actions) {
-        fprintf(stderr,
-                "warning: no actions specified (use --help for help)\n");
+
+    if (optind >= argc) {
+        ovs_fatal(0, "at least one non-option argument is required "
+                  "(use --help for help)");
+    }
+
+    return target ? target : "ovs-vswitchd";
+}
+
+static struct unixctl_client *
+connect_to_target(const char *target)
+{
+    struct unixctl_client *client;
+    char *socket_name;
+    int error;
+
+    if (target[0] != '/') {
+        char *pidfile_name;
+        pid_t pid;
+
+        pidfile_name = xasprintf("%s/%s.pid", ovs_rundir, target);
+        pid = read_pidfile(pidfile_name);
+        if (pid < 0) {
+            ovs_fatal(-pid, "cannot read pidfile \"%s\"", pidfile_name);
+        }
+        free(pidfile_name);
+        socket_name = xasprintf("%s/%s.%ld.ctl",
+                                ovs_rundir, target, (long int) pid);
+    } else {
+        socket_name = xstrdup(target);
+    }
+
+    error = unixctl_client_create(socket_name, &client);
+    if (error) {
+        ovs_fatal(error, "cannot connect to \"%s\"", socket_name);
     }
-    exit(ok ? 0 : 1);
+    free(socket_name);
+
+    return client;
 }
+
index 314da18..30ab52d 100644 (file)
@@ -53,7 +53,7 @@ struct switch_ {
 static bool learn_macs = true;
 
 /* Set up flows?  (If not, every packet is processed at the controller.) */
-static bool setup_flows = true;
+static bool set_up_flows = true;
 
 /* --max-idle: Maximum idle time, in seconds, before flows expire. */
 static int max_idle = 60;
@@ -202,7 +202,7 @@ new_switch(struct switch_ *sw, struct vconn *vconn, const char *name)
 {
     sw->rconn = rconn_new_from_vconn(name, vconn);
     sw->lswitch = lswitch_create(sw->rconn, learn_macs,
-                                 setup_flows ? max_idle : -1);
+                                 set_up_flows ? max_idle : -1);
 }
 
 static int
@@ -268,7 +268,7 @@ parse_options(int argc, char *argv[])
             break;
 
         case 'n':
-            setup_flows = false;
+            set_up_flows = false;
             break;
 
         case OPT_MUTE:
index 5a0da82..8e9ed3d 100644 (file)
@@ -371,9 +371,11 @@ show_dpif(struct dpif *dpif)
         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("\tlookups: frags:%llu, hit:%llu, missed:%llu, lost:%llu\n",
+               (unsigned long long int) stats.n_frags,
+               (unsigned long long int) stats.n_hit,
+               (unsigned long long int) stats.n_missed,
+               (unsigned long long int) stats.n_lost);
         printf("\tqueues: max-miss:%"PRIu16", max-action:%"PRIu16"\n",
                stats.max_miss_queue, stats.max_action_queue);
     }
index 2e2b38b..439d3f5 100644 (file)
@@ -352,6 +352,18 @@ Sets the source Ethernet address to \fImac\fR.
 
 .IP \fBmod_dl_dst\fB:\fImac\fR
 Sets the destination Ethernet address to \fImac\fR.
+
+.IP \fBmod_nw_src\fB:\fIip\fR
+Sets the IPv4 source address to \fIip\fR.
+
+.IP \fBmod_nw_dst\fB:\fIip\fR
+Sets the IPv4 destination address to \fIip\fR.
+
+.IP \fBmod_tp_src\fB:\fIport\fR
+Sets the TCP or UDP source port to \fIport\fR.
+
+.IP \fBmod_tp_dst\fB:\fIport\fR
+Sets the TCP or UDP destination port to \fIport\fR.
 .RE
 
 .IP
index e873ed7..db6038a 100644 (file)
@@ -599,6 +599,22 @@ str_to_action(char *str, struct ofpbuf *b)
             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, "mod_nw_src")) {
+            struct ofp_action_nw_addr *na;
+            na = put_action(b, sizeof *na, OFPAT_SET_NW_SRC);
+            str_to_ip(arg, &na->nw_addr);
+        } else if (!strcasecmp(act, "mod_nw_dst")) {
+            struct ofp_action_nw_addr *na;
+            na = put_action(b, sizeof *na, OFPAT_SET_NW_DST);
+            str_to_ip(arg, &na->nw_addr);
+        } else if (!strcasecmp(act, "mod_tp_src")) {
+            struct ofp_action_tp_port *ta;
+            ta = put_action(b, sizeof *ta, OFPAT_SET_TP_SRC);
+            ta->tp_port = htons(str_to_u32(arg));
+        } else if (!strcasecmp(act, "mod_tp_dst")) {
+            struct ofp_action_tp_port *ta;
+            ta = put_action(b, sizeof *ta, OFPAT_SET_TP_DST);
+            ta->tp_port = htons(str_to_u32(arg));
         } else if (!strcasecmp(act, "output")) {
             put_output_action(b, str_to_u32(arg));
         } else if (!strcasecmp(act, "drop")) {
@@ -918,11 +934,15 @@ do_mod_flows(const struct settings *s, int argc UNUSED, char *argv[])
     struct vconn *vconn;
     struct ofpbuf *buffer;
     struct ofp_flow_mod *ofm;
+    struct ofp_match match;
 
-    /* Parse and send. */
-    ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
-    str_to_flow(argv[2], &ofm->match, buffer,
+    /* 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;
     if (s->strict) {
         ofm->command = htons(OFPFC_MODIFY_STRICT);
     } else {
@@ -1133,7 +1153,7 @@ do_ping(const struct settings *s UNUSED, int argc, char *argv[])
             printf("Reply:\n");
             ofp_print(stdout, reply, reply->size, 2);
         }
-        printf("%d bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
+        printf("%zu 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)));
index 3d25574..312e7f7 100644 (file)
@@ -298,11 +298,16 @@ time is 8 seconds.
 
 .TP
 \fB-l\fR, \fB--listen=\fImethod\fR
-Configures the switch to additionally listen for incoming OpenFlow
-connections for switch management with \fBovs\-ofctl\fR.  The \fImethod\fR
+By default, the switch listens for OpenFlow management connections on a
+Unix domain socket named \fB@RUNDIR@/\fIdatapath\fB.mgmt\fR.  This socket 
+can be used to perform local OpenFlow monitoring and administration with
+tools such as \fBovs\-ofctl\fR.  
+
+This option may be used to override the default listener.  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.
+multiple connection methods.  If a single \fImethod\fR of \fBnone\fR is
+used, no listeners will be created.
 
 .RS
 .TP
index 603e258..d6b2c51 100644 (file)
@@ -114,6 +114,7 @@ main(int argc, char *argv[])
     struct ofproto *ofproto;
     struct ofsettings s;
     int error;
+    struct netflow_options nf_options;
 
     set_program_name(argv[0]);
     register_fault_handlers();
@@ -155,6 +156,12 @@ main(int argc, char *argv[])
         ofproto_set_mgmt_id(ofproto, s.mgmt_id);
     }
     ofproto_set_desc(ofproto, s.mfr_desc, s.hw_desc, s.sw_desc, s.serial_desc);
+    if (!s.listeners.n) {
+        svec_add_nocopy(&s.listeners, xasprintf("punix:%s/%s.mgmt",
+                                              ovs_rundir, s.dp_name));
+    } else if (s.listeners.n == 1 && !strcmp(s.listeners.names[0], "none")) {
+        svec_clear(&s.listeners);
+    }
     error = ofproto_set_listeners(ofproto, &s.listeners);
     if (error) {
         ovs_fatal(error, "failed to configure management connections");
@@ -164,7 +171,9 @@ main(int argc, char *argv[])
         ovs_fatal(error,
                   "failed to configure controller snooping connections");
     }
-    error = ofproto_set_netflow(ofproto, &s.netflow, 0, 0, false);
+    memset(&nf_options, 0, sizeof nf_options);
+    nf_options.collectors = s.netflow;
+    error = ofproto_set_netflow(ofproto, &nf_options);
     if (error) {
         ovs_fatal(error, "failed to configure NetFlow collectors");
     }
index b70f6c8..2eea7fd 100644 (file)
@@ -4,7 +4,7 @@
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-vsctl 8 "September 2009" "Open vSwitch" "Open vSwitch Manual"
+.TH ovs\-vsctl 8 "November 2009" "Open vSwitch" "Open vSwitch Manual"
 .ds PN ovs\-vsctl
 .
 .SH NAME
@@ -12,6 +12,7 @@ ovs\-vsctl \- utility for querying and configuring \fBovs\-vswitchd\fR
 .
 .SH SYNOPSIS
 \fBovs\-vsctl\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR\&...]
+[\fB\-\-\fR \fIcommand \fR[\fIargs\fR\&...]]
 .
 .SH DESCRIPTION
 The \fBovs\-vsctl\fR program configures \fBovs\-vswitchd\fR(8), mainly
@@ -22,7 +23,8 @@ when \fBovs\-vswitchd\fR is running, but it can also be used when
 changes will only take effect when \fBovs\-vswitchd\fR is started.
 .PP
 By default, each time \fBovs\-vsctl\fR runs, it examines and,
-depending on the requested command, possibly applies changes to an
+depending on the requested command or commands, possibly applies
+changes to an
 \fBovs\-vswitchd.conf\fR file.  Then, if it applied any changes and if
 \fBovs\-vswitchd\fR is running, it tells \fBovs\-vswitchd\fR to reload
 the modified configuration file and waits for the reload to complete
@@ -66,34 +68,39 @@ it bypasses the Open vSwitch configuration file locking protocol.
 .IP "\fB\-t \fItarget\fR"
 .IQ "\fB\-\-target=\fItarget\fR"
 Configures how \fBovs\-vsctl\fR contacts \fBovs\-vswitchd\fR to
-instruct it to reload its configuration file.  The \fItarget\fR takes
-one of two forms:
-.RS
-.IP \(bu
-The name of a Unix domain socket on which \fBovs\-vswitchd\fR is
-listening for control channel connections.  By default,
-\fBovs\-vswitchd\fR listens on a Unix domain socket named
-\fB@RUNDIR@/ovs\-vswitchd.\fIpid\fR.ctl\fR, where \fIpid\fR is the
-\fBovs\-vswitchd\fR process's process ID.
-.IP \(bu
-The name of a pidfile, that is, a file whose contents are the process
-ID of a running process as a decimal number.  \fBovs\-vswitchd\fR
-creates a pidfile if it is invoked with the \fB\-\-pidfile\fR option.
-\fBovs\-vsctl\fR reads the pidfile, then looks for a Unix socket named
-\fB@RUNDIR@/ovs\-vswitchd.\fIpid\fR.ctl\fR, where \fIpid\fR is
+instruct it to reload its configuration file.
+.IP
+If \fItarget\fR begins with \fB/\fR it must name a Unix domain socket
+on which \fBovs\-vswitchd\fR is listening for control channel
+connections.  By default, \fBovs\-vswitchd\fR listens on a Unix domain
+socket named \fB@RUNDIR@/ovs\-vswitchd.\fIpid\fB.ctl\fR, where
+\fIpid\fR is \fBovs\-vswitchd\fR's process ID.
+.IP
+Otherwise, \fBovs\-appctl\fR looks for a pidfile, that is, a file
+whose contents are the process ID of a running process as a decimal
+number, named \fB@RUNDIR@/\fItarget\fB.pid\fR.  (The \fB\-\-pidfile\fR
+option makes an Open vSwitch daemon create a pidfile.)
+\fBovs\-appctl\fR reads the pidfile, then looks for a Unix socket
+named \fB@RUNDIR@/\fItarget\fB.\fIpid\fB.ctl\fR, where \fIpid\fR is
 replaced by the process ID read from \fItarget\fR, and uses that file
 as if it had been specified directly as the target.
-.RE
-.IP
-If \fItarget\fR does not begin with \fB/\fR, then \fB@RUNDIR@/\fR is
-implicitly prefixed to it.
 .IP
-If neither \fB\-t\fR nor \fB\-\-target\fR is specified, the default target is
-\fB@RUNDIR@/ovs\-vswitchd.pid\fR.
+The default target is \fBovs\-vswitchd\fR.
 .IP "\fB\-\-no\-reload\fR"
 Prevents \fBovs\-vsctl\fR from telling \fBovs\-vswitchd\fR to reload
 its configuration file.
 .
+.IP "\fB\-\-no\-syslog\fR"
+By default, \fBovs\-vsctl\fR logs its arguments and the details of any
+changes that it makes to the system log.  This option disables this
+logging.
+.IP "\fB\-\-oneline\fR"
+Modifies the output format so that the output for each command is printed
+on a single line.  New-line characters that would otherwise separate
+lines are printed as \fB\\n\fR, and any instances of \fB\\\fR that
+would otherwise appear in the output are doubled.
+Prints a blank line for each command that has no output.
+.
 .SH COMMANDS
 The commands implemented by \fBovs\-vsctl\fR are described in the
 sections below.
@@ -152,8 +159,10 @@ Creates on \fIbridge\fR a new port named \fIport\fR that bonds
 together the network devices given as each \fIiface\fR.  At least two
 interfaces must be named.
 .
-.IP "\fBdel\-port \fIbridge port\fR"
-Deletes \fBport\fR from \fIbridge\fR.
+.IP "\fBdel\-port \fR[\fIbridge\fR] \fIport\fR"
+Deletes \fIport\fR.  If \fIbridge\fR is omitted, \fIport\fR is removed
+from whatever bridge contains it; if \fIbridge\fR is specified, it
+must be the real or fake bridge that contains \fIport\fR.
 .
 .IP "\fBport\-to\-br \fIport\fR"
 Prints the name of the bridge that contains \fIport\fR on standard
@@ -173,6 +182,16 @@ list.
 .IP "\fBiface\-to\-br \fIiface\fR"
 Prints the name of the bridge that contains \fIiface\fR on standard
 output.
+.SH "EXAMPLES"
+Create a new bridge named br0 and add port eth0 to it:
+.IP
+.B "ovs-vsctl add\-br br0"
+.br
+.B "ovs-vsctl add\-port br0 eth0"
+.PP
+Alternatively, perform both operations in a single atomic transaction:
+.IP 
+.B "ovs-vsctl add\-br br0 \-\- add\-port br0 eth0"
 .
 .SH "EXIT STATUS"
 .IP "0"
index 675f9dd..bef868c 100755 (executable)
@@ -22,24 +22,31 @@ import re
 import socket
 import stat
 import sys
+import syslog
 
 argv0 = sys.argv[0]
 if argv0.find('/') >= 0:
     argv0 = argv0[argv0.rfind('/') + 1:]
 
 DEFAULT_VSWITCHD_CONF = "@sysconfdir@/ovs-vswitchd.conf"
-VSWITCHD_CONF = DEFAULT_VSWITCHD_CONF
+vswitchd_conf = DEFAULT_VSWITCHD_CONF
 
-DEFAULT_VSWITCHD_TARGET = "@RUNDIR@/ovs-vswitchd.pid"
-VSWITCHD_TARGET = DEFAULT_VSWITCHD_TARGET
+DEFAULT_VSWITCHD_TARGET = "ovs-vswitchd"
+vswitchd_target = DEFAULT_VSWITCHD_TARGET
 
-RELOAD_VSWITCHD = True
+reload_vswitchd = True
+
+enable_syslog = True
 
 class Error(Exception):
     def __init__(self, msg):
         Exception.__init__(self)
         self.msg = msg
 
+def log(message):
+    if enable_syslog:
+        syslog.syslog(message)
+
 # XXX Most of the functions below should be integrated into a
 # VSwitchConfiguration object with logically named fields and methods
 # instead of this mishmash of functionality.
@@ -110,20 +117,52 @@ def cfg_read(filename, lock=False):
         if key not in cfg:
             cfg[key] = []
         cfg[key].append(value)
+
+    global orig_cfg
+    orig_cfg = cfg_clone(cfg)
+
     return cfg
 
+# Returns a deep copy of 'cfg', which must be in the format returned
+# by cfg_read().
+def cfg_clone(cfg):
+    new = {}
+    for key in cfg:
+        new[key] = list(cfg[key])
+    return new
+
+# Returns a list of all the configuration lines that are in 'a' but
+# not in 'b'.
+def cfg_subtract(a, b):
+    difference = []
+    for key in a:
+        for value in a[key]:
+            if key not in b or value not in b[key]:
+                difference.append("%s=%s" % (key, value))
+    return difference
+
 def do_cfg_save(cfg, file):
+    # Log changes.
+    added = cfg_subtract(cfg, orig_cfg)
+    removed = cfg_subtract(orig_cfg, cfg)
+    if added or removed:
+        log("configuration changes:")
+        for line in removed:
+            log("-%s\n" % line)
+        for line in added:
+            log("+%s\n" % line)
+
+    # Write changes.
     for key in sorted(cfg.keys()):
         for value in sorted(cfg[key]):
             file.write("%s=%s\n" % (key, value))
 
 def cfg_reload():
     target = VSWITCHD_TARGET
+    if not target.startswith('/'):
+        pid = read_first_line_of_file('%s/%s.pid' % ('@RUNDIR@', target))
+        target = '%s/%s.%s.ctl' % ('@RUNDIR@', target, pid)
     s = os.stat(target)
-    if stat.S_ISREG(s.st_mode):
-        pid = read_first_line_of_file(target)
-        target = "@RUNDIR@/ovs-vswitchd.%s.ctl" % pid
-        s = os.stat(target)
     if not stat.S_ISSOCK(s.st_mode):
         raise Error("%s is not a Unix domain socket, cannot reload" % target)
     skt = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
@@ -143,7 +182,7 @@ def cfg_save(cfg, filename):
         do_cfg_save(cfg, f)
         f.close()
         os.rename(tmp_name, filename)
-        if RELOAD_VSWITCHD:
+        if reload_vswitchd:
             cfg_reload()
 
 # Returns a set of the immediate subsections of 'section' within 'cfg'.  For
@@ -270,6 +309,14 @@ def del_port(cfg, port):
         if fnmatch.fnmatch(key, 'bridge.*.port'):
             cfg[key] = [s for s in cfg[key] if s != port]
 
+# Returns the name of the (real or fake) bridge in 'cfg' that contains
+# port 'port', or None if there is no such port.
+def port_to_bridge(cfg, port):
+    for bridge, parent, vlan in get_bridge_info(cfg):
+        if port != bridge and port in get_bridge_ports(cfg, parent, vlan):
+            return bridge
+    return None
+
 def usage():
     print """%(argv0)s: ovs-vswitchd management utility
 usage: %(argv0)s [OPTIONS] COMMAND [ARG...]
@@ -287,7 +334,7 @@ Port commands:
   list-ports BRIDGE           print the names of all the ports on BRIDGE
   add-port BRIDGE PORT        add network device PORT to BRIDGE
   add-bond BRIDGE PORT IFACE...  add new bonded port PORT in BRIDGE from IFACES
-  del-port BRIDGE PORT        delete PORT (which may be bonded) from BRIDGE
+  del-port [BRIDGE] PORT      delete PORT (which may be bonded) from BRIDGE
   port-to-br PORT             print name of bridge that contains PORT
 A bond is considered to be a single port.
 
@@ -297,9 +344,10 @@ Interface commands (a bond consists of multiple interfaces):
 A bond is considered to consist of interfaces.
 
 General options:
+  --no-syslog                 do not write mesages to syslog
   -c, --config=FILE           set configuration file
                               (default: %(config)s)
-  -t, --target=PIDFILE|SOCKET set ovs-vswitchd target
+  -t, --target=PROGRAM|SOCKET set ovs-vswitchd target
                               (default: %(target)s)
   --no-reload                 do not make ovs-vswitchd reload its configuration
   -h, --help                  display this help message and exit
@@ -324,9 +372,7 @@ def check_conflicts(cfg, name, op):
         if name in get_bridge_ifaces(cfg, parent, vlan):
             raise Error("%s because an interface named %s already exists on bridge %s" % (op, name, bridge))
     
-def cmd_add_br(bridge, parent=None, vlan=None):
-    cfg = cfg_read(VSWITCHD_CONF, True)
-
+def cmd_add_br(cfg, bridge, parent=None, vlan=None):
     check_conflicts(cfg, bridge, "cannot create a bridge named %s" % bridge)
     
     if parent and vlan:
@@ -349,10 +395,8 @@ def cmd_add_br(bridge, parent=None, vlan=None):
         cfg['bridge.%s.port' % parent].append(bridge)
     else:
         cfg['bridge.%s.port' % bridge] = [bridge]
-    cfg_save(cfg, VSWITCHD_CONF)
 
-def cmd_del_br(bridge):
-    cfg = cfg_read(VSWITCHD_CONF, True)
+def cmd_del_br(cfg, bridge):
     parent, vlan = find_bridge(cfg, bridge)
     if vlan == 0:
         vlan = -1
@@ -360,24 +404,21 @@ def cmd_del_br(bridge):
         del_port(cfg, port)
     if vlan < 0: 
         del_matching_keys(cfg, 'bridge.%s.[!0-9]*' % bridge)
-    cfg_save(cfg, VSWITCHD_CONF)
 
-def cmd_list_br():
-    cfg = cfg_read(VSWITCHD_CONF)
-    for bridge in get_all_bridges(cfg):
-        print bridge
+def cmd_list_br(cfg):
+    return get_all_bridges(cfg)
 
-def cmd_br_exists(bridge):
-    cfg = cfg_read(VSWITCHD_CONF)
+def cmd_br_exists(cfg, bridge):
     if bridge not in get_all_bridges(cfg):
         sys.exit(2)
 
-def cmd_list_ports(bridge):
-    cfg = cfg_read(VSWITCHD_CONF)
+def cmd_list_ports(cfg, bridge):
+    ports = []
     parent, vlan = find_bridge(cfg, bridge)
     for port in get_bridge_ports(cfg, parent, vlan):
         if port != bridge:
-            print port
+            ports.append(port)
+    return ports
 
 def do_add_port(cfg, bridge, parent, port, vlan):
     check_conflicts(cfg, port, "cannot create a port named %s" % port)
@@ -385,132 +426,180 @@ def do_add_port(cfg, bridge, parent, port, vlan):
     if vlan > 0:
         cfg['vlan.%s.tag' % port] = [vlan]
 
-def cmd_add_port(bridge, port):
-    cfg = cfg_read(VSWITCHD_CONF, True)
+def cmd_add_port(cfg, bridge, port):
     parent, vlan = find_bridge(cfg, bridge)
     do_add_port(cfg, bridge, parent, port, vlan)
-    cfg_save(cfg, VSWITCHD_CONF)
 
-def cmd_add_bond(bridge, port, *slaves):
-    cfg = cfg_read(VSWITCHD_CONF, True)
+def cmd_add_bond(cfg, bridge, port, *slaves):
     parent, vlan = find_bridge(cfg, bridge)
     do_add_port(cfg, bridge, parent, port, vlan)
     cfg['bonding.%s.slave' % port] = list(slaves)
-    cfg_save(cfg, VSWITCHD_CONF)
 
-def cmd_del_port(bridge, port):
-    cfg = cfg_read(VSWITCHD_CONF, True)
-    parent, vlan = find_bridge(cfg, bridge)
-    if port not in get_bridge_ports(cfg, parent, vlan):
-        if port in get_bridge_ports(cfg, parent, -1):
-            raise Error("bridge %s does not have a port %s (although its parent bridge %s does)" % (bridge, port, parent))
-        else:
-            raise Error("bridge %s does not have a port %s" % (bridge, port))
+def cmd_del_port(cfg, *args):
+    if len(args) == 2:
+        bridge, port = args
+        parent, vlan = find_bridge(cfg, bridge)
+        if port not in get_bridge_ports(cfg, parent, vlan):
+            if port in get_bridge_ports(cfg, parent, -1):
+                raise Error("bridge %s does not have a port %s (although its parent bridge %s does)" % (bridge, port, parent))
+            else:
+                raise Error("bridge %s does not have a port %s" % (bridge, port))
+    else:
+        port, = args
+        if not port_to_bridge(cfg, port):
+            raise Error("no port %s on any bridge" % port)
     del_port(cfg, port)
-    cfg_save(cfg, VSWITCHD_CONF)
 
-def cmd_port_to_br(port):
-    cfg = cfg_read(VSWITCHD_CONF)
-    for bridge, parent, vlan in get_bridge_info(cfg):
-        if port != bridge and port in get_bridge_ports(cfg, parent, vlan):
-            print bridge
-            return
-    raise Error("no port named %s" % port)
+def cmd_port_to_br(cfg, port):
+    bridge = port_to_bridge(cfg, port)
+    if bridge:
+        return (bridge,)
+    else:
+        raise Error("no port named %s" % port)
 
-def cmd_list_ifaces(bridge):
-    cfg = cfg_read(VSWITCHD_CONF)
+def cmd_list_ifaces(cfg, bridge):
+    ifaces = []
     parent, vlan = find_bridge(cfg, bridge)
     for iface in get_bridge_ifaces(cfg, parent, vlan):
         if iface != bridge:
-            print iface
+            ifaces.append(iface)
+    return ifaces
 
-def cmd_iface_to_br(iface):
-    cfg = cfg_read(VSWITCHD_CONF)
+def cmd_iface_to_br(cfg, iface):
     for bridge, parent, vlan in get_bridge_info(cfg):
         if iface != bridge and iface in get_bridge_ifaces(cfg, parent, vlan):
-            print bridge
-            return
+            return (bridge,)
     raise Error("no interface named %s" % iface)
 
-def cmd_br_to_vlan(bridge):
-    cfg = cfg_read(VSWITCHD_CONF)
+def cmd_br_to_vlan(cfg, bridge):
     parent, vlan = find_bridge(cfg, bridge)
-    print vlan
+    return (vlan,)
 
-def cmd_br_to_parent(bridge):
-    cfg = cfg_read(VSWITCHD_CONF)
+def cmd_br_to_parent(cfg, bridge):
     parent, vlan = find_bridge(cfg, bridge)
-    print parent
+    return (parent,)
     
+cmdTable = {'add-br': (cmd_add_br, True, lambda n: n == 1 or n == 3),
+            'del-br': (cmd_del_br, True, 1),
+            'list-br': (cmd_list_br, False, 0),
+            'br-exists': (cmd_br_exists, False, 1),
+            'list-ports': (cmd_list_ports, False, 1),
+            'add-port': (cmd_add_port, True, 2),
+            'add-bond': (cmd_add_bond, True, lambda n: n >= 4),
+            'del-port': (cmd_del_port, True, lambda n: n == 1 or n == 2),
+            'port-to-br': (cmd_port_to_br, False, 1),
+            'br-to-vlan': (cmd_br_to_vlan, False, 1),
+            'br-to-parent': (cmd_br_to_parent, False, 1),
+            'list-ifaces': (cmd_list_ifaces, False, 1),
+            'iface-to-br': (cmd_iface_to_br, False, 1)}
+
+# Break up commands at -- boundaries.
+def split_commands(args):
+    commands = []
+    command = []
+    for arg in args:
+        if arg == '--':
+            if command:
+                commands.append(command)
+            command = []
+        else:
+            command.append(arg)
+    if command:
+        commands.append(command)
+    return commands
+
+def check_command(args):
+    command, args = args[0], args[1:]
+    if command not in cmdTable:
+        sys.stderr.write("%s: unknown command '%s' (use --help for help)\n"
+                         % (argv0, command))
+        sys.exit(1)
+
+    function, is_mutator, nargs = cmdTable[command]
+    if callable(nargs) and not nargs(len(args)):
+        sys.stderr.write("%s: '%s' command does not accept %d arguments (use --help for help)\n" % (argv0, command, len(args)))
+        sys.exit(1)
+    elif not callable(nargs) and len(args) != nargs:
+        sys.stderr.write("%s: '%s' command takes %d arguments but %d were supplied (use --help for help)\n" % (argv0, command, nargs, len(args)))
+        sys.exit(1)
+
+def run_command(cfg, args):
+    command, args = args[0], args[1:]
+    function, need_lock, nargs = cmdTable[command]
+    return function(cfg, *args)
+
 def main():
     # Parse command line.
     try:
-        options, args = getopt.gnu_getopt(sys.argv[1:], "c:t:hV",
-                                          ["config=",
-                                           "target=",
-                                           "no-reload",
-                                           "help",
-                                           "version"])
+        options, args = getopt.getopt(sys.argv[1:], "c:t:hV",
+                                      ["config=",
+                                       "target=",
+                                       "no-reload",
+                                       "no-syslog",
+                                       "oneline",
+                                       "help",
+                                       "version"])
     except getopt.GetoptError, msg:
         sys.stderr.write("%s: %s (use --help for help)\n" % (argv0, msg))
         sys.exit(1)
 
     # Handle options.
+    oneline = False
     for opt, optarg in options:
         if opt == "-c" or opt == "--config":
-            global VSWITCHD_CONF
-            VSWITCHD_CONF = optarg
+            global vswitchd_conf
+            vswitchd_conf = optarg
         elif opt == "-t" or opt == "--target":
-            global VSWITCHD_TARGET
-            if optarg[0] != '/':
-                optarg = '@RUNDIR@/' + optarg
-            VSWITCHD_TARGET = optarg
+            global vswitchd_target
+            vswitchd_target = optarg
         elif opt == "--no-reload":
-            global RELOAD_VSWITCHD
-            RELOAD_VSWITCHD = False
+            global reload_vswitchd
+            reload_vswitchd = False
         elif opt == "-h" or opt == "--help":
             usage()
         elif opt == "-V" or opt == "--version":
             version()
+        elif opt == "--no-syslog":
+            global enable_syslog
+            enable_syslog = False
+        elif opt == "--oneline":
+            oneline = True
         else:
             raise RuntimeError("unhandled option %s" % opt)
 
-    # Execute commands.
-    if not args:
+    if enable_syslog:
+        syslog.openlog("ovs-vsctl")
+        log("Called as %s" % ' '.join(sys.argv[1:]))
+
+    # Break arguments into a series of commands.
+    commands = split_commands(args)
+    if not commands:
         sys.stderr.write("%s: missing command name (use --help for help)\n"
                          % argv0)
         sys.exit(1)
 
-    commands = {'add-br': (cmd_add_br, lambda n: n == 1 or n == 3),
-                'del-br': (cmd_del_br, 1),
-                'list-br': (cmd_list_br, 0),
-                'br-exists': (cmd_br_exists, 1),
-                'list-ports': (cmd_list_ports, 1),
-                'add-port': (cmd_add_port, 2),
-                'add-bond': (cmd_add_bond, lambda n: n >= 4),
-                'del-port': (cmd_del_port, 2),
-                'port-to-br': (cmd_port_to_br, 1),
-                'br-to-vlan': (cmd_br_to_vlan, 1),
-                'br-to-parent': (cmd_br_to_parent, 1),
-                'list-ifaces': (cmd_list_ifaces, 1),
-                'iface-to-br': (cmd_iface_to_br, 1)}
-    command = args[0]
-    args = args[1:]
-    if command not in commands:
-        sys.stderr.write("%s: unknown command '%s' (use --help for help)\n"
-                         % (argv0, command))
-        sys.exit(1)
+    # Check command syntax.
+    need_lock = False
+    for command in commands:
+        check_command(command)
+        if cmdTable[command[0]][1]:
+            need_lock = True
 
-    function, nargs = commands[command]
-    if callable(nargs) and not nargs(len(args)):
-        sys.stderr.write("%s: '%s' command does not accept %d arguments (use --help for help)\n" % (argv0, command, len(args)))
-        sys.exit(1)
-    elif not callable(nargs) and len(args) != nargs:
-        sys.stderr.write("%s: '%s' command takes %d arguments but %d were supplied (use --help for help)\n" % (argv0, command, nargs, len(args)))
-        sys.exit(1)
-    else:
-        function(*args)
-        sys.exit(0)
+    # Execute commands.
+    cfg = cfg_read(vswitchd_conf, need_lock)
+    for command in commands:
+        output = run_command(cfg, command)
+        if oneline:
+            if output == None:
+                output = ()
+            print '\\n'.join([str(s).replace('\\', '\\\\')
+                              for s in output])
+        elif output != None:
+            for line in output:
+                print line
+    if need_lock:
+        cfg_save(cfg, vswitchd_conf)
+    sys.exit(0)
 
 if __name__ == "__main__":
     try:
index 49a4158..3001756 100644 (file)
@@ -83,12 +83,12 @@ received on other slaves are dropped.  Otherwise, every multicast
 packet would be duplicated, once for every bond slave, because the
 physical switch attached to the bond will flood those packets.
 
-Bonding also drops some multicast packets received on the active
-slave: those for the vswitch has learned that the packet's MAC is on a
-port other than the bond port itself.  This is because it is likely
-that the vswitch itself sent the multicast packet out the bond port,
-on a slave other than the active slave, and is now receiving the
-packet back on the active slave.  However, the vswitch makes an
+Bonding also drops received packets when the vswitch has learned that
+the packet's MAC is on a port other than the bond port itself.  This is
+because it is likely that the vswitch itself sent the packet out the
+bond port on a different slave and is now receiving the packet back.
+This occurs when the packet is multicast or the physical switch has not
+yet learned the MAC and is flooding it.  However, the vswitch makes an
 exception to this rule for broadcast ARP replies, which indicate that
 the MAC has moved to another switch, probably due to VM migration.
 (ARP replies are normally unicast, so this exception does not match
@@ -121,8 +121,9 @@ more heavily than data sent less recently.  It considers each of the
 slaves in order from most-loaded to least-loaded.  If highly loaded
 slave H is significantly more heavily loaded than the least-loaded
 slave L, and slave H carries at least two hashes, then vswitchd shifts
-one of H's hashes to L.  However, vswitchd will not shift a hash from
-H to L if that will cause L's load to exceed H's load.
+one of H's hashes to L.  However, vswitchd will only shift a hash from
+H to L if it will decrease the ratio of the load between H and L by at
+least 0.1.
 
 Currently, "significantly more loaded" means that H must carry at
 least 1 Mbps more traffic, and that traffic must be at least 3%
index bece252..5494dea 100644 (file)
@@ -43,6 +43,7 @@
 #include "odp-util.h"
 #include "ofp-print.h"
 #include "ofpbuf.h"
+#include "ofproto/netflow.h"
 #include "ofproto/ofproto.h"
 #include "packets.h"
 #include "poll-loop.h"
@@ -147,7 +148,7 @@ struct port {
 struct bridge {
     struct list node;           /* Node in global list of bridges. */
     char *name;                 /* User-specified arbitrary name. */
-    struct mac_learning *ml;    /* MAC learning table, or null not to learn. */
+    struct mac_learning *ml;    /* MAC learning table. */
     bool sent_config_request;   /* Successfully sent config request? */
     uint8_t default_ea[ETH_ADDR_LEN]; /* Default MAC. */
 
@@ -217,6 +218,7 @@ static void bond_run(struct bridge *);
 static void bond_wait(struct bridge *);
 static void bond_rebalance_port(struct port *);
 static void bond_send_learning_packets(struct port *);
+static void bond_enable_slave(struct iface *iface, bool enable);
 
 static void port_create(struct bridge *, const char *name);
 static void port_reconfigure(struct port *);
@@ -569,9 +571,7 @@ bridge_reconfigure(void)
         uint64_t dpid;
         struct iface *local_iface;
         struct iface *hw_addr_iface;
-        uint8_t engine_type, engine_id;
-        bool add_id_to_iface = false;
-        struct svec nf_hosts;
+        struct netflow_options nf_options;
 
         bridge_fetch_dp_ifaces(br);
         iterate_and_prune_ifaces(br, init_iface_netdev, NULL);
@@ -595,36 +595,46 @@ bridge_reconfigure(void)
         ofproto_set_datapath_id(br->ofproto, dpid);
 
         /* Set NetFlow configuration on this bridge. */
-        dpif_get_netflow_ids(br->dpif, &engine_type, &engine_id);
+        memset(&nf_options, 0, sizeof nf_options);
+        dpif_get_netflow_ids(br->dpif, &nf_options.engine_type,
+                             &nf_options.engine_id);
+        nf_options.active_timeout = -1;
+
         if (cfg_has("netflow.%s.engine-type", br->name)) {
-            engine_type = cfg_get_int(0, "netflow.%s.engine-type", 
+            nf_options.engine_type = cfg_get_int(0, "netflow.%s.engine-type", 
                     br->name);
         }
         if (cfg_has("netflow.%s.engine-id", br->name)) {
-            engine_id = cfg_get_int(0, "netflow.%s.engine-id", br->name);
+            nf_options.engine_id = cfg_get_int(0, "netflow.%s.engine-id",
+                                               br->name);
+        }
+        if (cfg_has("netflow.%s.active-timeout", br->name)) {
+            nf_options.active_timeout = cfg_get_int(0,
+                                                    "netflow.%s.active-timeout",
+                                                    br->name);
         }
         if (cfg_has("netflow.%s.add-id-to-iface", br->name)) {
-            add_id_to_iface = cfg_get_bool(0, "netflow.%s.add-id-to-iface",
-                    br->name);
+            nf_options.add_id_to_iface = cfg_get_bool(0,
+                                                   "netflow.%s.add-id-to-iface",
+                                                    br->name);
         }
-        if (add_id_to_iface && engine_id > 0x7f) {
+        if (nf_options.add_id_to_iface && nf_options.engine_id > 0x7f) {
             VLOG_WARN("bridge %s: netflow port mangling may conflict with "
                     "another vswitch, choose an engine id less than 128", 
                     br->name);
         }
-        if (add_id_to_iface && br->n_ports > 0x1ff) {
+        if (nf_options.add_id_to_iface && br->n_ports > 508) {
             VLOG_WARN("bridge %s: netflow port mangling will conflict with "
-                    "another port when 512 or more ports are used", 
+                    "another port when more than 508 ports are used", 
                     br->name);
         }
-        svec_init(&nf_hosts);
-        cfg_get_all_keys(&nf_hosts, "netflow.%s.host", br->name);
-        if (ofproto_set_netflow(br->ofproto, &nf_hosts,  engine_type, 
-                    engine_id, add_id_to_iface)) {
+        svec_init(&nf_options.collectors);
+        cfg_get_all_keys(&nf_options.collectors, "netflow.%s.host", br->name);
+        if (ofproto_set_netflow(br->ofproto, &nf_options)) {
             VLOG_ERR("bridge %s: problem setting netflow collectors", 
                     br->name);
         }
-        svec_destroy(&nf_hosts);
+        svec_destroy(&nf_options.collectors);
 
         /* Update the controller and related settings.  It would be more
          * straightforward to call this from bridge_reconfigure_one(), but we
@@ -884,9 +894,7 @@ bridge_wait(void)
             continue;
         }
 
-        if (br->ml) {
-            mac_learning_wait(br->ml);
-        }
+        mac_learning_wait(br->ml);
         bond_wait(br);
         brstp_wait(br);
     }
@@ -899,9 +907,7 @@ bridge_flush(struct bridge *br)
 {
     COVERAGE_INC(bridge_flush);
     br->flush = true;
-    if (br->ml) {
-        mac_learning_flush(br->ml);
-    }
+    mac_learning_flush(br->ml);
 }
 
 /* Returns the 'br' interface for the ODPP_LOCAL port, or null if 'br' has no
@@ -930,6 +936,7 @@ bridge_unixctl_fdb_show(struct unixctl_conn *conn, const char *args)
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
     const struct bridge *br;
+    const struct mac_entry *e;
 
     br = bridge_lookup(args);
     if (!br) {
@@ -938,16 +945,13 @@ bridge_unixctl_fdb_show(struct unixctl_conn *conn, const char *args)
     }
 
     ds_put_cstr(&ds, " port  VLAN  MAC                Age\n");
-    if (br->ml) {
-        const struct mac_entry *e;
-        LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
-            if (e->port < 0 || e->port >= br->n_ports) {
-                continue;
-            }
-            ds_put_format(&ds, "%5d  %4d  "ETH_ADDR_FMT"  %3d\n",
-                          br->ports[e->port]->ifaces[0]->dp_ifidx,
-                          e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
+    LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
+        if (e->port < 0 || e->port >= br->n_ports) {
+            continue;
         }
+        ds_put_format(&ds, "%5d  %4d  "ETH_ADDR_FMT"  %3d\n",
+                      br->ports[e->port]->ifaces[0]->dp_ifidx,
+                      e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
     }
     unixctl_command_reply(conn, 200, ds_cstr(&ds));
     ds_destroy(&ds);
@@ -1089,9 +1093,7 @@ bridge_run_one(struct bridge *br)
         return error;
     }
 
-    if (br->ml) {
-        mac_learning_run(br->ml, ofproto_get_revalidate_set(br->ofproto));
-    }
+    mac_learning_run(br->ml, ofproto_get_revalidate_set(br->ofproto));
     bond_run(br);
     brstp_run(br);
 
@@ -1475,13 +1477,31 @@ lookup_bond_entry(const struct port *port, const uint8_t mac[ETH_ADDR_LEN])
 static int
 bond_choose_iface(const struct port *port)
 {
-    size_t i;
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+    size_t i, best_down_slave = -1;
+    long long next_delay_expiration = LLONG_MAX;
+
     for (i = 0; i < port->n_ifaces; i++) {
-        if (port->ifaces[i]->enabled) {
+        struct iface *iface = port->ifaces[i];
+
+        if (iface->enabled) {
             return i;
+        } else if (iface->delay_expires < next_delay_expiration) {
+            best_down_slave = i;
+            next_delay_expiration = iface->delay_expires;
         }
     }
-    return -1;
+
+    if (best_down_slave != -1) {
+        struct iface *iface = port->ifaces[best_down_slave];
+
+        VLOG_INFO_RL(&rl, "interface %s: skipping remaining %lli ms updelay "
+                     "since no other interface is up", iface->name,
+                     iface->delay_expires - time_msec());
+        bond_enable_slave(iface, true);
+    }
+
+    return best_down_slave;
 }
 
 static bool
@@ -1531,10 +1551,12 @@ bond_link_status_update(struct iface *iface, bool carrier)
         iface->delay_expires = LLONG_MAX;
         VLOG_INFO_RL(&rl, "interface %s: will not be %s",
                      iface->name, carrier ? "disabled" : "enabled");
-    } else if (carrier && port->updelay && port->active_iface < 0) {
-        iface->delay_expires = time_msec();
-        VLOG_INFO_RL(&rl, "interface %s: skipping %d ms updelay since no "
-                     "other interface is up", iface->name, port->updelay);
+    } else if (carrier && port->active_iface < 0) {
+        bond_enable_slave(iface, true);
+        if (port->updelay) {
+            VLOG_INFO_RL(&rl, "interface %s: skipping %d ms updelay since no "
+                         "other interface is up", iface->name, port->updelay);
+        }
     } else {
         int delay = carrier ? port->updelay : port->downdelay;
         iface->delay_expires = time_msec() + delay;
@@ -1571,6 +1593,12 @@ bond_enable_slave(struct iface *iface, bool enable)
     struct port *port = iface->port;
     struct bridge *br = port->bridge;
 
+    /* This acts as a recursion check.  If the act of disabling a slave
+     * causes a different slave to be enabled, the flag will allow us to
+     * skip redundant work when we reenter this function.  It must be
+     * cleared on exit to keep things safe with multiple bonds. */
+    static bool moving_active_iface = false;
+
     iface->delay_expires = LLONG_MAX;
     if (enable == iface->enabled) {
         return;
@@ -1583,19 +1611,29 @@ bond_enable_slave(struct iface *iface, bool enable)
         if (iface->port_ifidx == port->active_iface) {
             ofproto_revalidate(br->ofproto,
                                port->active_iface_tag);
+
+            /* Disabling a slave can lead to another slave being immediately
+             * enabled if there will be no active slaves but one is waiting
+             * on an updelay.  In this case we do not need to run most of the
+             * code for the newly enabled slave since there was no period
+             * without an active slave and it is redundant with the disabling
+             * path. */
+            moving_active_iface = true;
             bond_choose_active_iface(port);
         }
         bond_send_learning_packets(port);
     } else {
         VLOG_WARN("interface %s: enabled", iface->name);
-        if (port->active_iface < 0) {
+        if (port->active_iface < 0 && !moving_active_iface) {
             ofproto_revalidate(br->ofproto, port->no_ifaces_tag);
             bond_choose_active_iface(port);
             bond_send_learning_packets(port);
         }
         iface->tag = tag_create_random();
     }
-    port_update_bond_compat(port);
+
+    moving_active_iface = false;
+    port->bond_compat_is_stale = true;
 }
 
 static void
@@ -1606,20 +1644,19 @@ bond_run(struct bridge *br)
     for (i = 0; i < br->n_ports; i++) {
         struct port *port = br->ports[i];
 
+        if (port->n_ifaces >= 2) {
+            for (j = 0; j < port->n_ifaces; j++) {
+                struct iface *iface = port->ifaces[j];
+                if (time_msec() >= iface->delay_expires) {
+                    bond_enable_slave(iface, !iface->enabled);
+                }
+            }
+        }
+
         if (port->bond_compat_is_stale) {
             port->bond_compat_is_stale = false;
             port_update_bond_compat(port);
         }
-
-        if (port->n_ifaces < 2) {
-            continue;
-        }
-        for (j = 0; j < port->n_ifaces; j++) {
-            struct iface *iface = port->ifaces[j];
-            if (time_msec() >= iface->delay_expires) {
-                bond_enable_slave(iface, !iface->enabled);
-            }
-        }
     }
 }
 
@@ -1745,7 +1782,7 @@ port_includes_vlan(const struct port *port, uint16_t vlan)
 static size_t
 compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
              const struct port *in_port, const struct port *out_port,
-             struct dst dsts[], tag_type *tags)
+             struct dst dsts[], tag_type *tags, uint16_t *nf_output_iface)
 {
     mirror_mask_t mirrors = in_port->src_mirrors;
     struct dst *dst = dsts;
@@ -1764,7 +1801,9 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
                 dst++;
             }
         }
+        *nf_output_iface = NF_OUT_FLOOD;
     } else if (out_port && set_dst(dst, flow, in_port, out_port, tags)) {
+        *nf_output_iface = dst->dp_ifidx;
         mirrors |= out_port->dst_mirrors;
         dst++;
     }
@@ -1783,14 +1822,26 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
                     if (port_includes_vlan(port, m->out_vlan)
                         && set_dst(dst, flow, in_port, port, tags))
                     {
+                        int flow_vlan;
+
                         if (port->vlan < 0) {
                             dst->vlan = m->out_vlan;
                         }
                         if (dst_is_duplicate(dsts, dst - dsts, dst)) {
                             continue;
                         }
-                        if (dst->dp_ifidx == flow->in_port
-                            && dst->vlan == vlan) {
+
+                        /* Use the vlan tag on the original flow instead of
+                         * the one passed in the vlan parameter.  This ensures
+                         * that we compare the vlan from before any implicit
+                         * tagging tags place. This is necessary because
+                         * dst->vlan is the final vlan, after removing implicit
+                         * tags. */
+                        flow_vlan = ntohs(flow->dl_vlan);
+                        if (flow_vlan == 0) {
+                            flow_vlan = OFP_VLAN_NONE;
+                        }
+                        if (port == in_port && dst->vlan == flow_vlan) {
                             /* Don't send out input port on same VLAN. */
                             continue;
                         }
@@ -1820,14 +1871,16 @@ print_dsts(const struct dst *dsts, size_t n)
 static void
 compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
                 const struct port *in_port, const struct port *out_port,
-                tag_type *tags, struct odp_actions *actions)
+                tag_type *tags, struct odp_actions *actions,
+                uint16_t *nf_output_iface)
 {
     struct dst dsts[DP_MAX_PORTS * (MAX_MIRRORS + 1)];
     size_t n_dsts;
     const struct dst *p;
     uint16_t cur_vlan;
 
-    n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, tags);
+    n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, tags,
+                          nf_output_iface);
 
     cur_vlan = ntohs(flow->dl_vlan);
     for (p = dsts; p < &dsts[n_dsts]; p++) {
@@ -1846,14 +1899,77 @@ compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
     }
 }
 
+/* Returns the effective vlan of a packet, taking into account both the
+ * 802.1Q header and implicitly tagged ports.  A value of 0 indicates that
+ * the packet is untagged and -1 indicates it has an invalid header and
+ * should be dropped. */
+static int flow_get_vlan(struct bridge *br, const flow_t *flow,
+                         struct port *in_port, bool have_packet)
+{
+    /* Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet
+     * belongs to VLAN 0, so we should treat both cases identically.  (In the
+     * former case, the packet has an 802.1Q header that specifies VLAN 0,
+     * presumably to allow a priority to be specified.  In the latter case, the
+     * packet does not have any 802.1Q header.) */
+    int vlan = ntohs(flow->dl_vlan);
+    if (vlan == OFP_VLAN_NONE) {
+        vlan = 0;
+    }
+    if (in_port->vlan >= 0) {
+        if (vlan) {
+            /* XXX support double tagging? */
+            if (have_packet) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
+                             "packet received on port %s configured with "
+                             "implicit VLAN %"PRIu16,
+                             br->name, ntohs(flow->dl_vlan),
+                             in_port->name, in_port->vlan);
+            }
+            return -1;
+        }
+        vlan = in_port->vlan;
+    } else {
+        if (!port_includes_vlan(in_port, vlan)) {
+            if (have_packet) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
+                             "packet received on port %s not configured for "
+                             "trunking VLAN %d",
+                             br->name, vlan, in_port->name, vlan);
+            }
+            return -1;
+        }
+    }
+
+    return vlan;
+}
+
+static void
+update_learning_table(struct bridge *br, const flow_t *flow, int vlan,
+                      struct port *in_port)
+{
+    tag_type rev_tag = mac_learning_learn(br->ml, flow->dl_src,
+                                          vlan, in_port->port_idx);
+    if (rev_tag) {
+        /* The log messages here could actually be useful in debugging,
+         * so keep the rate limit relatively high. */
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30,
+                                                                300);
+        VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
+                    "on port %s in VLAN %d",
+                    br->name, ETH_ADDR_ARGS(flow->dl_src),
+                    in_port->name, vlan);
+        ofproto_revalidate(br->ofproto, rev_tag);
+    }
+}
+
 static bool
-is_bcast_arp_reply(const flow_t *flow, const struct ofpbuf *packet)
+is_bcast_arp_reply(const flow_t *flow)
 {
-    struct arp_eth_header *arp = (struct arp_eth_header *) packet->data;
     return (flow->dl_type == htons(ETH_TYPE_ARP)
-            && eth_addr_is_broadcast(flow->dl_dst)
-            && packet->size >= sizeof(struct arp_eth_header)
-            && arp->ar_op == ARP_OP_REQUEST);
+            && flow->nw_proto == ARP_OP_REPLY
+            && eth_addr_is_broadcast(flow->dl_dst));
 }
 
 /* If the composed actions may be applied to any packet in the given 'flow',
@@ -1862,12 +1978,13 @@ is_bcast_arp_reply(const flow_t *flow, const struct ofpbuf *packet)
 static bool
 process_flow(struct bridge *br, const flow_t *flow,
              const struct ofpbuf *packet, struct odp_actions *actions,
-             tag_type *tags)
+             tag_type *tags, uint16_t *nf_output_iface)
 {
     struct iface *in_iface;
     struct port *in_port;
     struct port *out_port = NULL; /* By default, drop the packet/flow. */
     int vlan;
+    int out_port_idx;
 
     /* Find the interface and port structure for the received packet. */
     in_iface = iface_from_dp_ifidx(br, flow->in_port);
@@ -1895,41 +2012,9 @@ process_flow(struct bridge *br, const flow_t *flow,
         return true;
     }
     in_port = in_iface->port;
-
-    /* Figure out what VLAN this packet belongs to.
-     *
-     * Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet
-     * belongs to VLAN 0, so we should treat both cases identically.  (In the
-     * former case, the packet has an 802.1Q header that specifies VLAN 0,
-     * presumably to allow a priority to be specified.  In the latter case, the
-     * packet does not have any 802.1Q header.) */
-    vlan = ntohs(flow->dl_vlan);
-    if (vlan == OFP_VLAN_NONE) {
-        vlan = 0;
-    }
-    if (in_port->vlan >= 0) {
-        if (vlan) {
-            /* XXX support double tagging? */
-            if (packet != NULL) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
-                             "packet received on port %s configured with "
-                             "implicit VLAN %"PRIu16,
-                             br->name, ntohs(flow->dl_vlan),
-                             in_port->name, in_port->vlan);
-            }
-            goto done;
-        }
-        vlan = in_port->vlan;
-    } else {
-        if (!port_includes_vlan(in_port, vlan)) {
-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-            VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
-                         "packet received on port %s not configured for "
-                         "trunking VLAN %d",
-                         br->name, vlan, in_port->name, vlan);
-            goto done;
-        }
+    vlan = flow_get_vlan(br, flow, in_port, !!packet);
+    if (vlan < 0) {
+        goto done;
     }
 
     /* Drop frames for ports that STP wants entirely killed (both for
@@ -1953,63 +2038,48 @@ process_flow(struct bridge *br, const flow_t *flow,
         goto done;
     }
 
-    /* Multicast (and broadcast) packets on bonds need special attention, to
-     * avoid receiving duplicates. */
-    if (in_port->n_ifaces > 1 && eth_addr_is_multicast(flow->dl_dst)) {
-        *tags |= in_port->active_iface_tag;
-        if (in_port->active_iface != in_iface->port_ifidx) {
-            /* Drop all multicast packets on inactive slaves. */
-            goto done;
-        } else {
-            /* Drop all multicast packets for which we have learned a different
-             * input port, because we probably sent the packet on one slave
-             * and got it back on the active slave.  Broadcast ARP replies are
-             * an exception to this rule: the host has moved to another
-             * switch. */
-            int src_idx = mac_learning_lookup(br->ml, flow->dl_src, vlan);
-            if (src_idx != -1 && src_idx != in_port->port_idx) {
-                if (packet) {
-                    if (!is_bcast_arp_reply(flow, packet)) {
-                        goto done;
-                    }
-                } else {
-                    /* No way to know whether it's an ARP reply, because the
-                     * flow entry doesn't include enough information and we
-                     * don't have a packet.  Punt. */
-                    return false;
-                }
+    /* Packets received on bonds need special attention to avoid duplicates. */
+    if (in_port->n_ifaces > 1) {
+        int src_idx;
+
+        if (eth_addr_is_multicast(flow->dl_dst)) {
+            *tags |= in_port->active_iface_tag;
+            if (in_port->active_iface != in_iface->port_ifidx) {
+                /* Drop all multicast packets on inactive slaves. */
+                goto done;
             }
         }
+
+        /* Drop all packets for which we have learned a different input
+         * port, because we probably sent the packet on one slave and got
+         * it back on the other.  Broadcast ARP replies are an exception
+         * to this rule: the host has moved to another switch. */
+        src_idx = mac_learning_lookup(br->ml, flow->dl_src, vlan);
+        if (src_idx != -1 && src_idx != in_port->port_idx &&
+            !is_bcast_arp_reply(flow)) {
+                goto done;
+        }
     }
 
     /* MAC learning. */
     out_port = FLOOD_PORT;
-    if (br->ml) {
-        int out_port_idx;
-
-        /* Learn source MAC (but don't try to learn from revalidation). */
-        if (packet) {
-            tag_type rev_tag = mac_learning_learn(br->ml, flow->dl_src,
-                                                  vlan, in_port->port_idx);
-            if (rev_tag) {
-                /* The log messages here could actually be useful in debugging,
-                 * so keep the rate limit relatively high. */
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30,
-                                                                        300);
-                VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
-                            "on port %s in VLAN %d",
-                            br->name, ETH_ADDR_ARGS(flow->dl_src),
-                            in_port->name, vlan);
-                ofproto_revalidate(br->ofproto, rev_tag);
-            }
-        }
-
-        /* Determine output port. */
-        out_port_idx = mac_learning_lookup_tag(br->ml, flow->dl_dst, vlan,
-                                               tags);
-        if (out_port_idx >= 0 && out_port_idx < br->n_ports) {
-            out_port = br->ports[out_port_idx];
-        }
+    /* Learn source MAC (but don't try to learn from revalidation). */
+    if (packet) {
+        update_learning_table(br, flow, vlan, in_port);
+    }
+
+    /* Determine output port. */
+    out_port_idx = mac_learning_lookup_tag(br->ml, flow->dl_dst, vlan,
+                                           tags);
+    if (out_port_idx >= 0 && out_port_idx < br->n_ports) {
+        out_port = br->ports[out_port_idx];
+    } else if (!packet && !eth_addr_is_multicast(flow->dl_dst)) {
+        /* If we are revalidating but don't have a learning entry then
+         * eject the flow.  Installing a flow that floods packets opens
+         * up a window of time where we could learn from a packet reflected
+         * on a bond and blackhole packets before the learning table is
+         * updated to reflect the correct port. */
+        return false;
     }
 
     /* Don't send packets out their input ports.  Don't forward frames that STP
@@ -2019,17 +2089,10 @@ process_flow(struct bridge *br, const flow_t *flow,
     }
 
 done:
-    compose_actions(br, flow, vlan, in_port, out_port, tags, actions);
+    compose_actions(br, flow, vlan, in_port, out_port, tags, actions,
+                    nf_output_iface);
 
-    /*
-     * We send out only a single packet, instead of setting up a flow, if the
-     * packet is an ARP directed to broadcast that arrived on a bonded
-     * interface.  In such a situation ARP requests and replies must be handled
-     * differently, but OpenFlow unfortunately can't distinguish them.
-     */
-    return (in_port->n_ifaces < 2
-            || flow->dl_type != htons(ETH_TYPE_ARP)
-            || !eth_addr_is_broadcast(flow->dl_dst));
+    return true;
 }
 
 /* Careful: 'opp' is in host byte order and opp->port_no is an OFP port
@@ -2071,7 +2134,8 @@ bridge_port_changed_ofhook_cb(enum ofp_port_reason reason,
 
 static bool
 bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
-                        struct odp_actions *actions, tag_type *tags, void *br_)
+                        struct odp_actions *actions, tag_type *tags,
+                        uint16_t *nf_output_iface, void *br_)
 {
     struct bridge *br = br_;
 
@@ -2084,7 +2148,7 @@ bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
 #endif
 
     COVERAGE_INC(bridge_process_flow);
-    return process_flow(br, flow, packet, actions, tags);
+    return process_flow(br, flow, packet, actions, tags, nf_output_iface);
 }
 
 static void
@@ -2094,17 +2158,30 @@ bridge_account_flow_ofhook_cb(const flow_t *flow,
                               void *br_)
 {
     struct bridge *br = br_;
+    struct port *in_port;
     const union odp_action *a;
 
+    /* Feed information from the active flows back into the learning table
+     * to ensure that table is always in sync with what is actually flowing
+     * through the datapath. */
+    in_port = port_from_dp_ifidx(br, flow->in_port);
+    if (in_port) {
+        int vlan = flow_get_vlan(br, flow, in_port, false);
+         if (vlan >= 0) {
+            update_learning_table(br, flow, vlan, in_port);
+        }
+    }
+
     if (!br->has_bonded_ports) {
         return;
     }
 
     for (a = actions; a < &actions[n_actions]; a++) {
         if (a->type == ODPAT_OUTPUT) {
-            struct port *port = port_from_dp_ifidx(br, a->output.port);
-            if (port && port->n_ifaces >= 2) {
-                struct bond_entry *e = lookup_bond_entry(port, flow->dl_src);
+            struct port *out_port = port_from_dp_ifidx(br, a->output.port);
+            if (out_port && out_port->n_ifaces >= 2) {
+                struct bond_entry *e = lookup_bond_entry(out_port,
+                                                         flow->dl_src);
                 e->tx_bytes += n_bytes;
             }
         }
@@ -2259,8 +2336,9 @@ log_bals(const struct slave_balance *bals, size_t n_bals, struct port *port)
 /* Shifts 'hash' from 'from' to 'to' within 'port'. */
 static void
 bond_shift_load(struct slave_balance *from, struct slave_balance *to,
-                struct bond_entry *hash)
+                int hash_idx)
 {
+    struct bond_entry *hash = from->hashes[hash_idx];
     struct port *port = from->iface->port;
     uint64_t delta = hash->tx_bytes;
 
@@ -2278,12 +2356,11 @@ bond_shift_load(struct slave_balance *from, struct slave_balance *to,
      * it require more work, the only purpose it would be to allow that hash to
      * be migrated to another slave in this rebalancing run, and there is no
      * point in doing that.  */
-    if (from->hashes[0] == hash) {
+    if (hash_idx == 0) {
         from->hashes++;
     } else {
-        int i = hash - from->hashes[0];
-        memmove(from->hashes + i, from->hashes + i + 1,
-                (from->n_hashes - (i + 1)) * sizeof *from->hashes);
+        memmove(from->hashes + hash_idx, from->hashes + hash_idx + 1,
+                (from->n_hashes - (hash_idx + 1)) * sizeof *from->hashes);
     }
     from->n_hashes--;
 
@@ -2368,22 +2445,60 @@ bond_rebalance_port(struct port *port)
             /* 'from' is carrying significantly more load than 'to', and that
              * load is split across at least two different hashes.  Pick a hash
              * to migrate to 'to' (the least-loaded slave), given that doing so
-             * must not cause 'to''s load to exceed 'from''s load.
+             * must decrease the ratio of the load on the two slaves by at
+             * least 0.1.
              *
              * The sort order we use means that we prefer to shift away the
              * smallest hashes instead of the biggest ones.  There is little
              * reason behind this decision; we could use the opposite sort
              * order to shift away big hashes ahead of small ones. */
             size_t i;
+            bool order_swapped;
 
             for (i = 0; i < from->n_hashes; i++) {
+                double old_ratio, new_ratio;
                 uint64_t delta = from->hashes[i]->tx_bytes;
-                if (to->tx_bytes + delta < from->tx_bytes - delta) {
+
+                if (delta == 0 || from->tx_bytes - delta == 0) {
+                    /* Pointless move. */
+                    continue;
+                }
+
+                order_swapped = from->tx_bytes - delta < to->tx_bytes + delta;
+
+                if (to->tx_bytes == 0) {
+                    /* Nothing on the new slave, move it. */
+                    break;
+                }
+
+                old_ratio = (double)from->tx_bytes / to->tx_bytes;
+                new_ratio = (double)(from->tx_bytes - delta) /
+                            (to->tx_bytes + delta);
+
+                if (new_ratio == 0) {
+                    /* Should already be covered but check to prevent division
+                     * by zero. */
+                    continue;
+                }
+
+                if (new_ratio < 1) {
+                    new_ratio = 1 / new_ratio;
+                }
+
+                if (old_ratio - new_ratio > 0.1) {
+                    /* Would decrease the ratio, move it. */
                     break;
                 }
             }
             if (i < from->n_hashes) {
-                bond_shift_load(from, to, from->hashes[i]);
+                bond_shift_load(from, to, i);
+                port->bond_compat_is_stale = true;
+
+                /* If the result of the migration changed the relative order of
+                 * 'from' and 'to' swap them back to maintain invariants. */
+                if (order_swapped) {
+                    swap_bals(from, to);
+                }
 
                 /* Re-sort 'bals'.  Note that this may make 'from' and 'to'
                  * point to different slave_balance structures.  It is only
@@ -2394,7 +2509,6 @@ bond_rebalance_port(struct port *port)
             } else {
                 from++;
             }
-            port->bond_compat_is_stale = true;
         }
     }
 
@@ -2413,7 +2527,7 @@ bond_send_learning_packets(struct port *port)
     struct ofpbuf packet;
     int error, n_packets, n_errors;
 
-    if (!port->n_ifaces || port->active_iface < 0 || !br->ml) {
+    if (!port->n_ifaces || port->active_iface < 0) {
         return;
     }
 
@@ -2564,14 +2678,10 @@ bond_unixctl_show(struct unixctl_conn *conn, const char *args)
                 continue;
             }
 
-            ds_put_format(&ds, "\thash %d: %lld kB load\n",
+            ds_put_format(&ds, "\thash %d: %"PRIu64" kB load\n",
                           hash, be->tx_bytes / 1024);
 
             /* MACs. */
-            if (!port->bridge->ml) {
-                break;
-            }
-
             LIST_FOR_EACH (me, struct mac_entry, lru_node,
                            &port->bridge->ml->lrus) {
                 uint16_t dp_ifidx;
@@ -3267,7 +3377,8 @@ static void
 mirror_reconfigure(struct bridge *br)
 {
     struct svec old_mirrors, new_mirrors;
-    size_t i;
+    size_t i, n_rspan_vlans;
+    unsigned long *rspan_vlans;
 
     /* Collect old and new mirrors. */
     svec_init(&old_mirrors);
@@ -3316,6 +3427,29 @@ mirror_reconfigure(struct bridge *br)
             m->out_port->is_mirror_output_port = true;
         }
     }
+
+    /* Update learning disabled vlans (for RSPAN). */
+    rspan_vlans = NULL;
+    n_rspan_vlans = cfg_count("vlan.%s.disable-learning", br->name);
+    if (n_rspan_vlans) {
+        rspan_vlans = bitmap_allocate(4096);
+
+        for (i = 0; i < n_rspan_vlans; i++) {
+            int vlan = cfg_get_vlan(i, "vlan.%s.disable-learning", br->name);
+            if (vlan >= 0) {
+                bitmap_set1(rspan_vlans, vlan);
+                VLOG_INFO("bridge %s: disabling learning on vlan %d\n",
+                          br->name, vlan);
+            } else {
+                VLOG_ERR("bridge %s: invalid value '%s' for learning disabled "
+                         "VLAN", br->name,
+                       cfg_get_string(i, "vlan.%s.disable-learning", br->name));
+            }
+        }
+    }
+    if (mac_learning_set_disabled_vlans(br->ml, rspan_vlans)) {
+        bridge_flush(br);
+    }
 }
 
 static void
index d15b4ba..8da640f 100644 (file)
@@ -700,7 +700,7 @@ recv_ofmp_extended_data(uint32_t xid, const struct ofmp_header *ofmph,
          * OpenFlow message. */
         new_oh = ofpbuf_at(&ext_data_buffer, 0, 65536);
         if (!new_oh) {
-            VLOG_WARN_RL(&rl, "received short embedded message: %d\n",
+            VLOG_WARN_RL(&rl, "received short embedded message: %zu\n",
                     ext_data_buffer.size);
             return -EINVAL;
         }
index cd911ca..e1d448d 100644 (file)
@@ -29,10 +29,8 @@ replaced by a single \fB%\fR.  The \fB%\fR character may not otherwise
 appear in \fIcommand\fR.
 .IP
 The commands that are substituted into \fIcommand\fR are those that
-can be listed by passing \fB-e help\fR to \fBovs\-appctl\fR with
-\fBovs\-vswitchd\fR as target.  The command that is substituted may
-include white space-separated arguments, so \fIcommand\fR should include
-shell quotes around \fB%s\fR.
+can be listed by passing \fBhelp\fR to \fBovs\-appctl\fR with
+\fBovs\-vswitchd\fR as target.
 .IP
 \fIcommand\fR must not redirect \fBovs\-appctl\fR's standard output or
 standard error streams, because \fBovs\-brcompatd\fR expects to read
index d4a59c3..99d08c5 100644 (file)
@@ -1165,10 +1165,7 @@ parse_options(int argc, char *argv[])
     char *short_options = long_options_to_short_options(long_options);
     int error;
 
-    appctl_command = xasprintf("%s/ovs-appctl -t "
-                               "%s/ovs-vswitchd.`cat %s/ovs-vswitchd.pid`.ctl "
-                               "-e '%%s'",
-                               ovs_bindir, ovs_rundir, ovs_rundir);
+    appctl_command = xasprintf("%s/ovs-appctl %%s", ovs_bindir);
     for (;;) {
         int c;
 
index 8579421..431c948 100644 (file)
@@ -99,6 +99,12 @@ These commands manage bridges.
 Lists each MAC address/VLAN pair learned by the specified \fIbridge\fR,
 along with the port on which it was learned and the age of the entry,
 in seconds.
+.
+.IP "\fBbridge/dump-flows\fR \fIbridge\fR"
+Lists all flows in \fIbridge\fR, including those normally hidden to
+commands such as \fBovs-ofctl dump-flows\fR.  Flows set up by mechanisms
+such as in-band control and fail-open are hidden from the controller
+since it is not allowed to modify or override them.
 .SS "BOND COMMANDS"
 These commands manage bonded ports on an Open vSwitch's bridges.  To
 understand some of these commands, it is important to understand a
index 4d4bb48..c416678 100644 (file)
@@ -346,12 +346,15 @@ on port 1, disrupting connectivity.  If mirroring to a VLAN is desired
 in this scenario, then the physical switch must be replaced by one
 that learns Ethernet addresses on a per-VLAN basis.  In addition,
 learning should be disabled on the VLAN containing mirrored traffic.
-If this is not done then the intermediate switch will learn the MAC
+If this is not done then intermediate switches will learn the MAC
 address of each end host from the mirrored traffic.  If packets being
 sent to that end host are also mirrored, then they will be dropped
 since the switch will attempt to send them out the input port.
 Disabling learning for the VLAN will cause the switch to correctly
-send the packet out all ports configured for that VLAN.
+send the packet out all ports configured for that VLAN.  If Open
+vSwitch is being used as an intermediate switch learning can be disabled
+by setting the key \fBvlan.\fIbrname\fB.learning-disable=\fIvid\fR
+to the mirrored VLAN.
 .ST "Example"
 The following \fBovs\-vswitchd\fR configuration copies all frames received
 on \fBeth1\fR or \fBeth2\fR to \fBeth3\fR.
@@ -411,18 +414,24 @@ port.eth1.ingress.policing-burst=20
 
 .fi
 .SS "NetFlow v5 Flow Logging"
-NetFlow is a protocol that exports a number of details about terminating 
-IP flows, such as the principals involved and duration.  A bridge may be 
-configured to send NetFlow v5 records to NetFlow collectors when flows 
-end.  To enable, define the key \fBnetflow.\fIbridge\fB.host\fR for each 
-collector in the form \fIip\fB:\fIport\fR.  Records from \fIbridge\fR 
+NetFlow is a protocol that exports a number of details about terminating
+IP flows, such as the principals involved and duration.  A bridge may be
+configured to send NetFlow v5 records to NetFlow collectors when flows
+end.  To enable, define the key \fBnetflow.\fIbridge\fB.host\fR for each
+collector in the form \fIip\fB:\fIport\fR.  Records from \fIbridge\fR
 will be sent to each \fIip\fR on UDP \fIport\fR.  The \fIip\fR must
 be specified numerically, not as a DNS name.
 
-The NetFlow messages will use the datapath index for the engine type and id.  
-This can be overridden with the \fBnetflow.\fIbridge\fB.engine-type\fR and 
+In addition to terminating flows, NetFlow can also send records at a set
+interval for flows that are still active.  This interval can be configured
+by defining the key \fBnetflow.\fIbridge\fB\.active-timeout\fR.  The value
+is in seconds.  An active timeout of 0 will disable this functionality.  By
+default there is timeout value of 600 seconds.
+
+The NetFlow messages will use the datapath index for the engine type and id.
+This can be overridden with the \fBnetflow.\fIbridge\fB.engine-type\fR and
 \fBnetflow.\fIbridge\fB.engine-id\fR, respectively.  Each takes a value
-between 0 and 255, inclusive. 
+between 0 and 255, inclusive.
 
 Many NetFlow collectors do not expect multiple switches to be
 sending messages from the same host, and they do not store the engine
@@ -430,8 +439,15 @@ information which could be used to disambiguate the traffic.  To prevent
 flows from multiple switches appearing as if they came on the interface,
 add \fBnetflow.\fIbridge\fB.add-id-to-iface=true\fR to the configuration
 file.  This will place the least significant 7 bits of the engine id
-into the most significant bits of the ingress and egress interface fields 
-of flow records.  By default, this behavior is disabled.
+into the most significant bits of the ingress and egress interface fields
+of flow records.  When this option is enabled, a maximum of 508 ports are
+supported.  By default, this behavior is disabled.
+
+The egress interface field normally contains the OpenFlow port number,
+however, certain port values have special meaning: 0xFFFF indicates
+flooding, 0xFFFE is multiple controller-specified output interfaces, and
+0xFFFD means that packets from the flow were dropped.  If add-id-to-iface
+is enabled then these values become 0x1FF, 0x1FE, and 0x1FD respectively.
 
 The following syntax sends NetFlow records for \fBmybr\fR to the NetFlow
 collector \fBnflow.example.com\fR on UDP port \fB9995\fR:
diff --git a/xenserver/LICENSE b/xenserver/LICENSE
new file mode 100644 (file)
index 0000000..ce8949e
--- /dev/null
@@ -0,0 +1,523 @@
+The files etc_xensource_scripts_vif and
+opt_xensource_libexec_interface-reconfigure are distributed under the
+terms of the GNU Lesser General Public License version 2.1 (included
+below).
+
+As a special exception to the GNU Lesser General Public License, you
+may link, statically or dynamically, a "work that uses the Library"
+with a publicly distributed version of the Library to produce an
+executable file containing portions of the Library, and distribute
+that executable file under terms of your choice, without any of the
+additional requirements listed in clause 6 of the GNU Lesser General
+Public License.  By "a publicly distributed version of the Library",
+we mean either the unmodified Library as distributed, or a
+modified version of the Library that is distributed under the
+conditions defined in clause 3 of the GNU Library General Public
+License.  This exception does not however invalidate any other reasons
+why the executable file might be covered by the GNU Lesser General
+Public License.
+
+------------
+
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
index c11f447..9240910 100644 (file)
@@ -48,6 +48,12 @@ files are:
 
         vswitch-aware replacement for Citrix script of the same name.
 
+    usr_share_vswitch_scripts_refresh-xs-network-uuids
+
+        Script to refresh bridge.<bridge>.xs-network-uuids keys, which
+        can get out-of-sync following a pool join.  Running this script
+        is an alternative to rebooting the host.
+
     root_vswitch_scripts_sysconfig.template
 
         Template for vswitch's /etc/sysconfig/vswitch configuration
index be0530e..73cc590 100644 (file)
@@ -20,4 +20,5 @@ EXTRA_DIST += \
        xenserver/usr_sbin_xen-bugtool \
        xenserver/usr_share_vswitch_scripts_sysconfig.template \
        xenserver/usr_share_vswitch_scripts_vif-on-internal-bridge \
+       xenserver/usr_share_vswitch_scripts_refresh-xs-network-uuids \
        xenserver/vswitch-xen.spec
index 74162dc..8847550 100755 (executable)
@@ -105,16 +105,13 @@ function remove_modules {
 
 function reload_vswitchd {
     if [ -f "$VSWITCHD_PIDFILE" ]; then
-        "$appctl" \
-            --target=ovs-vswitchd.$(cat "$VSWITCHD_PIDFILE").ctl \
-            --execute=vswitchd/reload
+        "$appctl" --target=/var/run/ovs-vswitchd.`cat $VSWITCHD_PIDFILE`.ctl vswitchd/reload
     fi
 }
 
 function reload_brcompatd {
     if [ -f "$BRCOMPATD_PIDFILE" ]; then
-        "$appctl" \
-            --target=ovs-brcompatd.$(cat "$BRCOMPATD_PIDFILE").ctl --reopen
+        "$appctl" --target=/var/run/ovs-brcompatd.`cat $BRCOMPATD_PIDFILE`.ctl vlog/reopen
     fi
 }
 
@@ -197,7 +194,7 @@ function start_brcompatd {
         valgrind_opt="valgrind --log-file=$BRCOMPATD_VALGRIND_LOG $BRCOMPATD_VALGRIND_OPT"
         daemonize="n"
     fi
-    appctl_cmd="$appctl -t /var/run/ovs-vswitchd.\`cat $VSWITCHD_PIDFILE\`.ctl -e '%s'"
+    appctl_cmd="$appctl --target=/var/run/ovs-vswitchd.\`cat $VSWITCHD_PIDFILE\`.ctl %s"
     if [ "$daemonize" != "y" ]; then
         # Start in background and force a "success" message
         action "Starting ovs-brcompatd ($strace_opt$valgrind_opt)" true
@@ -271,7 +268,8 @@ function start {
             '--del-match=port.*' \
             '--del-match=bonding.*' \
             '--del-match=iface.*' \
-            '--del-match=vlan.*'
+            '--del-match=vlan.*.trunks=*' \
+            '--del-match=vlan.*.tag=*'
     fi
 
     start_vswitchd
index fdfc320..cede29c 100755 (executable)
@@ -1,7 +1,17 @@
 #!/bin/sh
 
-# Copyright (C) 2008,2009 Citrix Systems, Inc. All rights reserved.
+# Copyright (C) 2008,2009 Citrix Systems, Inc.
 # Copyright (C) 2009 Nicira Networks, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; version 2.1 only. with the special
+# exception on linking described in file LICENSE.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
 
 # CA-23900: Warning: when VIFs are added to windows guests with PV drivers the backend vif device is registered,
 # unregistered and then registered again. This causes the udev event to fire twice and this script runs twice.
@@ -117,7 +127,7 @@ add_to_bridge()
     local vid=
     if [ "$VLAN_ID" -ne 0 ] ; then
        bridge=$($vsctl br-to-parent $bridge)
-       vid="--add=vlan.${dev}.tag=${VLAN_ID}"
+       vid="--add=vlan.${vif}.tag=${VLAN_ID}"
     fi
 
     if [ "$type" = "vif" ] ; then
@@ -132,7 +142,8 @@ add_to_bridge()
 
     $cfg_mod -F /etc/ovs-vswitchd.conf \
         --del-match="bridge.*.port=${dev}" \
-        --del-match="vlan.${dev}.[!0-9]*" \
+        --del-match="vlan.${dev}.trunks=*" \
+        --del-match="vlan.${dev}.tags=*" \
         --del-match="port.${dev}.[!0-9]*" \
         --add="bridge.$bridge.port=${dev}" \
         $vid $vif_details -c 
@@ -200,7 +211,8 @@ remove)
        logger -t scripts-vif "${dev} has been removed"
        $cfg_mod -vANY:console:emer -F /etc/ovs-vswitchd.conf \
            --del-match="bridge.*.port=${dev}" \
-           --del-match="vlan.${dev}.[!0-9]*" \
+           --del-match="vlan.${dev}.trunks=*" \
+           --del-match="vlan.${dev}.tag=*" \
            --del-match="port.${dev}.[!0-9]*" -c
        $service vswitch reload
        ;;
index e9f56dd..2b6f62b 100755 (executable)
@@ -1,8 +1,18 @@
 #!/usr/bin/python
 #
-# Copyright (c) 2008,2009 Citrix Systems, Inc. All rights reserved.
+# Copyright (c) 2008,2009 Citrix Systems, Inc.
 # Copyright (c) 2009 Nicira Networks.
 #
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; version 2.1 only. with the special
+# exception on linking described in file LICENSE.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
 """Usage:
 
     %(command-name)s <PIF> up
@@ -1250,7 +1260,8 @@ def datapath_deconfigure_ipdev(interface):
     return ['--del-match=bridge.*.port=%s' % interface,
             '--del-match=port.%s.[!0-9]*' % interface,
             '--del-match=iface.%s.[!0-9]*' % interface,
-            '--del-match=vlan.%s.[!0-9]*' % interface]
+            '--del-match=vlan.%s.trunks=*' % interface,
+            '--del-match=vlan.%s.tag=*' % interface]
 
 def datapath_modify_config(commands):
     if debug_mode():
@@ -1507,10 +1518,10 @@ def action_down(pif):
     ifdown(ipdev)
 
     if dp:
-        #nw = db.get_pif_record(pif)['network']
-        #nwrec = db.get_network_record(nw)
-        #cfgmod_argv += ['# deconfigure xs-network-uuids']
-        #cfgmod_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
+        nw = db.get_pif_record(pif)['network']
+        nwrec = db.get_network_record(nw)
+        cfgmod_argv += ['# deconfigure xs-network-uuids']
+        cfgmod_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
 
         log("deconfigure ipdev %s on %s" % (ipdev,bridge))
         cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
diff --git a/xenserver/usr_share_vswitch_scripts_refresh-xs-network-uuids b/xenserver/usr_share_vswitch_scripts_refresh-xs-network-uuids
new file mode 100755 (executable)
index 0000000..34fe1e7
--- /dev/null
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+. /etc/xensource-inventory
+
+for pif in $(xe pif-list --minimal host-uuid=${INSTALLATION_UUID} currently-attached=true VLAN=-1 | sed 's/,/ /g'); do
+    printf "Refreshing PIF %s...  " $pif
+    if /opt/xensource/libexec/interface-reconfigure --pif-uuid=$pif up; then
+        printf "done\n"
+    else
+        printf "error!\n"
+    fi
+done
index 9492011..e177f37 100644 (file)
@@ -73,6 +73,8 @@ install -m 755 xenserver/etc_xensource_scripts_vif \
              $RPM_BUILD_ROOT/usr/share/vswitch/scripts/vif
 install -m 755 xenserver/usr_share_vswitch_scripts_vif-on-internal-bridge \
              $RPM_BUILD_ROOT/usr/share/vswitch/scripts/vif-on-internal-bridge
+install -m 755 xenserver/usr_share_vswitch_scripts_refresh-xs-network-uuids \
+               $RPM_BUILD_ROOT/usr/share/vswitch/scripts/refresh-xs-network-uuids
 install -m 755 xenserver/usr_sbin_xen-bugtool \
              $RPM_BUILD_ROOT/usr/share/vswitch/scripts/xen-bugtool
 install -m 755 xenserver/usr_sbin_brctl \
@@ -309,6 +311,7 @@ fi
 /etc/profile.d/vswitch.sh
 /lib/modules/%{xen_version}/kernel/net/vswitch/openvswitch_mod.ko
 /lib/modules/%{xen_version}/kernel/net/vswitch/brcompat_mod.ko
+/usr/share/vswitch/scripts/refresh-xs-network-uuids
 /usr/share/vswitch/scripts/interface-reconfigure
 /usr/share/vswitch/scripts/vif
 /usr/share/vswitch/scripts/xen-bugtool