Merge "master" branch into "db".
authorBen Pfaff <blp@nicira.com>
Wed, 2 Dec 2009 19:49:53 +0000 (11:49 -0800)
committerBen Pfaff <blp@nicira.com>
Wed, 2 Dec 2009 19:49:53 +0000 (11:49 -0800)
84 files changed:
INSTALL.Linux
INSTALL.bridge
INSTALL.userspace [new file with mode: 0644]
Makefile.am
README
acinclude.m4
configure.ac
datapath/actions.c
datapath/actions.h
datapath/datapath.c
datapath/flow.c
datapath/flow.h
datapath/linux-2.6/Makefile.main.in
datapath/linux-2.6/compat-2.6/include/linux/skbuff.h
debian/openvswitch-switch.logrotate
extras/ezio/ovs-switchui.c
include/openflow/openflow.h
include/openvswitch/datapath-protocol.h
lib/backtrace.c
lib/classifier.h
lib/dpif-linux.c
lib/dpif-netdev.c
lib/dpif-provider.h
lib/dpif.c
lib/dpif.h
lib/fault.c
lib/flow.c
lib/leak-checker.c
lib/learning-switch.c
lib/learning-switch.h
lib/lockfile.c
lib/mac-learning.c
lib/mac-learning.h
lib/netdev-linux.c
lib/netdev-provider.h
lib/netdev.c
lib/netdev.h
lib/odp-util.c
lib/ofp-print.c
lib/pcap.c
lib/poll-loop.c
lib/socket-util.c
lib/socket-util.h
lib/stream-tcp.c
lib/svec.c
lib/svec.h
lib/timeval.c
lib/unixctl.c
lib/vconn-ssl.c
lib/vconn-tcp.c
lib/vconn.c
lib/vlog-modules.def
lib/vlog-unixctl.man
lib/vlog.man
ofproto/automake.mk
ofproto/collectors.c [new file with mode: 0644]
ofproto/collectors.h [new file with mode: 0644]
ofproto/in-band.c
ofproto/netflow.c
ofproto/netflow.h
ofproto/ofproto.c
ofproto/ofproto.h
utilities/ovs-appctl.8.in
utilities/ovs-appctl.c
utilities/ovs-controller.8.in
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/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/etc_init.d_vswitch
xenserver/etc_xensource_scripts_vif
xenserver/opt_xensource_libexec_interface-reconfigure
xenserver/usr_share_vswitch_scripts_dump-vif-details

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
 -------------
index 76d180f..b30152d 100644 (file)
@@ -54,8 +54,11 @@ to update system scripts to follow these steps.
 
 4. Start ovs-vswitchd and ovs-brcompatd, e.g.:
 
-      % ovs-vswitchd -P -D -vANY:console:EMER /etc/ovs-vswitchd.conf
-      % ovs-brcompatd -P -D -vANY:console:EMER /etc/ovs-vswitchd.conf
+      % ovs-vswitchd --pidfile --detach -vANY:console:EMER \
+                /etc/ovs-vswitchd.conf
+
+      % ovs-brcompatd --pidfile --detach -vANY:console:EMER \
+                /etc/ovs-vswitchd.conf
 
 5. Now you should be able to manage the Open vSwitch using brctl and
    related tools.  For example, you can create an Open vSwitch bridge,
diff --git a/INSTALL.userspace b/INSTALL.userspace
new file mode 100644 (file)
index 0000000..241d79c
--- /dev/null
@@ -0,0 +1,63 @@
+              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 with ovs-vswitchd
+----------------------------------------------
+
+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.
+
+Using the Userspace Datapath with ovs-openflowd
+-----------------------------------------------
+
+To use ovs-openflowd in userspace mode, specify a datapath name that
+begins with "netdev:", and specify --ports with the names of the ports
+that should be included in the datapath as argument.  For example:
+
+    ovs-openflowd netdev:br0 --ports=eth0,eth1,eth2
+
+ovs-openflowd will create a TAP device as the bridge's local
+interface, named the same as the bridge minus the "netdev:" prefix.
+
+Bug Reporting
+-------------
+
+Please report problems to bugs@openvswitch.org.
index 301fc7c..164ca3e 100644 (file)
@@ -16,6 +16,7 @@ AM_CPPFLAGS += -I $(top_srcdir)/include
 AM_CPPFLAGS += -I $(top_srcdir)/lib
 
 AM_CFLAGS = -Wstrict-prototypes
+AM_CFLAGS += $(WARNING_FLAGS)
 
 if NDEBUG
 AM_CPPFLAGS += -DNDEBUG
@@ -29,6 +30,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 6ba647a..e37a316 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
@@ -123,6 +144,13 @@ AC_DEFUN([OVS_CHECK_LINUX26_COMPAT], [
                   [OVS_DEFINE([HAVE_CSUM_UNFOLD])])
   OVS_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [skb_cow],
                   [OVS_DEFINE([HAVE_SKB_COW])])
+  # Check for the proto_data_valid member in struct sk_buff.  The [^@]
+  # is necessary because some versions of this header remove the
+  # member but retain the kerneldoc comment that describes it (which
+  # starts with @).  The brackets must be doubled because of m4
+  # quoting rules.
+  OVS_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [[[^@]]proto_data_valid],
+                  [OVS_DEFINE([HAVE_PROTO_DATA_VALID])])
   OVS_CHECK_LOG2_H
   OVS_CHECK_VETH
   if cmp -s datapath/linux-2.6/kcompat.h.new \
@@ -194,7 +222,7 @@ AC_DEFUN([OVS_CHECK_CC_OPTION],
      AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,)], [ovs_cv_name[]=yes], [ovs_cv_name[]=no])
      CFLAGS="$ovs_save_CFLAGS"])
   if test $ovs_cv_name = yes; then
-    m4_if([$2], [], [;], [$2])
+    m4_if([$2], [], [:], [$2])
   else
     m4_if([$3], [], [:], [$3])
   fi
@@ -202,8 +230,9 @@ AC_DEFUN([OVS_CHECK_CC_OPTION],
 
 dnl OVS_ENABLE_OPTION([OPTION])
 dnl Check whether the given C compiler OPTION is accepted.
-dnl If so, add it to CFLAGS.
+dnl If so, add it to WARNING_FLAGS.
 dnl Example: OVS_ENABLE_OPTION([-Wdeclaration-after-statement])
 AC_DEFUN([OVS_ENABLE_OPTION], 
-  [OVS_CHECK_CC_OPTION([$1], [CFLAGS="$CFLAGS $1"])])
+  [OVS_CHECK_CC_OPTION([$1], [WARNING_FLAGS="$WARNING_FLAGS $1"])
+   AC_SUBST([WARNING_FLAGS])])
 dnl ----------------------------------------------------------------------
index fe676d9..a94ff93 100644 (file)
@@ -80,7 +80,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..cadab05 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) && defined(HAVE_PROTO_DATA_VALID)
+                       /* 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 1ae1d77..2a8fb50 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))
@@ -574,7 +574,7 @@ static int dp_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb)
 #error
 #endif
 
-#if defined(CONFIG_XEN) && LINUX_VERSION_CODE == KERNEL_VERSION(2,6,18)
+#if defined(CONFIG_XEN) && defined(HAVE_PROTO_DATA_VALID)
 /* This code is copied verbatim from net/dev/core.c in Xen's
  * linux-2.6.18-92.1.10.el5.xs5.0.0.394.644.  We can't call those functions
  * directly because they aren't exported. */
@@ -975,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)))
@@ -1016,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:
@@ -1041,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;
        }
@@ -1062,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;
 
@@ -1162,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
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 2831721..dd9bfa3 100644 (file)
@@ -5,6 +5,17 @@
 
 #include <linux/version.h>
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+/* In version 2.6.24 the return type of skb_headroom() changed from 'int' to
+ * 'unsigned int'.  We use skb_headroom() as one arm of a min(a,b) invocation
+ * in make_writable() in actions.c, so we need the correct type. */
+#define skb_headroom rpl_skb_headroom
+static inline unsigned int rpl_skb_headroom(const struct sk_buff *skb)
+{
+       return skb->data - skb->head;
+}
+#endif
+
 #ifndef HAVE_SKB_COPY_FROM_LINEAR_DATA_OFFSET
 static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb,
                                                     const int offset, void *to,
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 e56f83e..bd07e86 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 8b095e9..194b04e 100644 (file)
  * fields after F tend to be wildcarded as well.  If this assumption is
  * violated, then the classifier will still classify flows correctly, but its
  * performance will suffer.
+ *
+ * The classifier uses a collection of CLS_N_FIELDS hash tables for wildcarded
+ * flows.  Each of these tables contains the flows that wildcard a given field
+ * and do not wildcard any of the fields that precede F in the ordering.  The
+ * key for each hash table is the value of the fields preceding F that are not
+ * wildcarded.  All the flows that fall within a table and have the same key
+ * are kept as a linked list ordered from highest to lowest priority.
+ *
+ * The classifier also maintains a separate hash table of exact-match flows.
+ *
+ * To search the classifier we first search the table of exact-match flows,
+ * since exact-match flows always have highest priority.  If there is a match,
+ * we're done.  Otherwise, we search each of the CLS_N_FIELDS hash tables in
+ * turn, looking for the highest-priority match, and return it (if any).
  */
 
 #include "flow.h"
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 8bd9648..816d402 100644 (file)
@@ -373,9 +373,13 @@ do_add_port(struct dp_netdev *dp, const char *devname, uint16_t flags,
     if (!internal) {
         error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev);
     } else {
-        char *tapname = xasprintf("tap:%s", devname);
-        error = netdev_open(tapname, NETDEV_ETH_TYPE_ANY, &netdev);
-        free(tapname);
+        error = netdev_create(devname, "tap", NULL);
+        if (!error) {
+            error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev);
+            if (error) {
+                netdev_destroy(devname);
+            }
+        }
     }
     if (error) {
         return error;
@@ -468,6 +472,7 @@ static int
 do_del_port(struct dp_netdev *dp, uint16_t port_no)
 {
     struct dp_netdev_port *port;
+    char *name;
     int error;
 
     error = get_port_by_number(dp, port_no, &port);
@@ -480,7 +485,12 @@ do_del_port(struct dp_netdev *dp, uint16_t port_no)
     dp->n_ports--;
     dp->serial++;
 
+    name = xstrdup(netdev_get_name(port->netdev));
     netdev_close(port->netdev);
+    if (port->internal) {
+        netdev_destroy(name);
+    }
+    free(name);
     free(port);
 
     return 0;
@@ -665,7 +675,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 +693,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 +711,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 +868,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 +888,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 bd159b2..020e017 100644 (file)
@@ -42,7 +42,7 @@ static inline void dpif_assert_class(const struct dpif *dpif,
 }
 
 /* Datapath interface class structure, to be defined by each implementation of
- * a datapath interface
+ * a datapath interface.
  *
  * These functions return 0 if successful or a positive errno value on failure,
  * except where otherwise noted.
@@ -52,7 +52,7 @@ static inline void dpif_assert_class(const struct dpif *dpif,
  * EWOULDBLOCK or EINPROGRESS.  We may relax this requirement in the future if
  * and when we encounter performance problems. */
 struct dpif_class {
-    /* Prefix for names of dpifs in this class, e.g. "udatapath:".
+    /* Prefix for names of dpifs in this class, e.g. "netdev:".
      *
      * One dpif class may have the empty string "" as its prefix, in which case
      * that dpif class is associated with dpif names that don't match any other
index 649c246..793eaa1 100644 (file)
@@ -169,13 +169,35 @@ dpif_open(const char *name, struct dpif **dpifp)
 /* Tries to create and open a new datapath with the given 'name'.  Will fail if
  * a datapath named 'name' already exists.  Returns 0 if successful, otherwise
  * a positive errno value.  On success stores a pointer to the datapath in
- * '*dpifp', otherwise a null pointer.*/
+ * '*dpifp', otherwise a null pointer. */
 int
 dpif_create(const char *name, struct dpif **dpifp)
 {
     return do_open(name, true, dpifp);
 }
 
+/* Tries to open a datapath with the given 'name', creating it if it does not
+ * exist.  Returns 0 if successful, otherwise a positive errno value.  On
+ * success stores a pointer to the datapath in '*dpifp', otherwise a null
+ * pointer. */
+int
+dpif_create_and_open(const char *name, struct dpif **dpifp)
+{
+    int error;
+
+    error = dpif_create(name, dpifp);
+    if (error == EEXIST || error == EBUSY) {
+        error = dpif_open(name, dpifp);
+        if (error) {
+            VLOG_WARN("datapath %s already exists but cannot be opened: %s",
+                      name, strerror(error));
+        }
+    } else if (error) {
+        VLOG_WARN("failed to create datapath %s: %s", name, strerror(error));
+    }
+    return error;
+}
+
 /* Closes and frees the connection to 'dpif'.  Does not destroy the datapath
  * itself; call dpif_delete() first, instead, if that is desirable. */
 void
index 216c099..1d109c2 100644 (file)
@@ -37,6 +37,7 @@ int dp_enumerate(struct svec *);
 
 int dpif_open(const char *name, struct dpif **);
 int dpif_create(const char *name, struct dpif **);
+int dpif_create_and_open(const char *name, struct dpif **);
 void dpif_close(struct dpif *);
 
 const char *dpif_name(const struct dpif *);
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 99d5ee4..78346ac 100644 (file)
@@ -57,6 +57,8 @@ struct lswitch {
     uint32_t capabilities;
     time_t last_features_request;
     struct mac_learning *ml;    /* NULL to act as hub instead of switch. */
+    bool exact_flows;           /* Use exact-match flows? */
+    bool action_normal;         /* Use OFPP_NORMAL? */
 
     /* Number of outgoing queued packets on the rconn. */
     struct rconn_packet_counter *queued;
@@ -105,7 +107,8 @@ static packet_handler_func process_stats_reply;
  *
  * 'rconn' is used to send out an OpenFlow features request. */
 struct lswitch *
-lswitch_create(struct rconn *rconn, bool learn_macs, int max_idle)
+lswitch_create(struct rconn *rconn, bool learn_macs,
+              bool exact_flows, int max_idle, bool action_normal)
 {
     struct lswitch *sw;
     size_t i;
@@ -115,6 +118,8 @@ lswitch_create(struct rconn *rconn, bool learn_macs, int max_idle)
     sw->datapath_id = 0;
     sw->last_features_request = time_now() - 1;
     sw->ml = learn_macs ? mac_learning_create() : NULL;
+    sw->action_normal = action_normal;
+    sw->exact_flows = exact_flows;
     sw->queued = rconn_packet_counter_create();
     sw->next_query = LLONG_MIN;
     sw->last_query = LLONG_MIN;
@@ -430,10 +435,34 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
         /* Don't send out packets on their input ports. */
         goto drop_it;
     } else if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
+        struct ofpbuf *buffer;
+        struct ofp_flow_mod *ofm;
+        uint32_t wildcards;
+
+        /* Check if we need to wildcard the flows. */
+        if (!sw->exact_flows) {
+            /* We can not wildcard all fields.
+             * We need in_port to detect moves.
+             * We need both SA and DA to do learning. */
+            wildcards = (OFPFW_DL_TYPE | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK
+                         | OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST);
+        } else {
+            /* Exact match */
+            wildcards = 0;
+        }
+
+        /* Check if we need to use "NORMAL" action. */
+        if (sw->action_normal && out_port != OFPP_FLOOD) {
+            out_port = OFPP_NORMAL;
+        }
+
         /* The output port is known, or we always flood everything, so add a
          * new flow. */
-        queue_tx(sw, rconn, make_add_simple_flow(&flow, ntohl(opi->buffer_id),
-                                                 out_port, sw->max_idle));
+        buffer = make_add_simple_flow(&flow, ntohl(opi->buffer_id),
+                                      out_port, sw->max_idle);
+        ofm = buffer->data;
+        ofm->match.wildcards = htonl(wildcards);
+        queue_tx(sw, rconn, buffer);
 
         /* If the switch didn't buffer the packet, we need to send a copy. */
         if (ntohl(opi->buffer_id) == UINT32_MAX) {
@@ -441,9 +470,15 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
                      make_unbuffered_packet_out(&pkt, in_port, out_port));
         }
     } else {
+        struct ofpbuf *b;
+
+        /* Check if we need to use "NORMAL" action. */
+        if (sw->action_normal && out_port != OFPP_FLOOD) {
+            out_port = OFPP_NORMAL;
+        }
+
         /* We don't know that MAC, or we don't set up flows.  Send along the
          * packet without setting up a flow. */
-        struct ofpbuf *b;
         if (ntohl(opi->buffer_id) == UINT32_MAX) {
             b = make_unbuffered_packet_out(&pkt, in_port, out_port);
         } else {
index 8837d64..2de862e 100644 (file)
@@ -22,7 +22,9 @@
 struct ofpbuf;
 struct rconn;
 
-struct lswitch *lswitch_create(struct rconn *, bool learn_macs, int max_idle);
+struct lswitch *lswitch_create(struct rconn *, bool learn_macs,
+                              bool exact_flows, int max_idle,
+                              bool action_normal);
 void lswitch_run(struct lswitch *, struct rconn *);
 void lswitch_wait(struct lswitch *);
 void lswitch_destroy(struct lswitch *);
index e5a041e..9bb7c6b 100644 (file)
@@ -61,8 +61,8 @@ lockfile_name(const char *file_name)
 {
     const char *slash = strrchr(file_name, '/');
     return (slash
-            ? xasprintf("%.*s/.%s.~lock~", slash - file_name, file_name,
-                        slash + 1)
+            ? xasprintf("%.*s/.%s.~lock~",
+                        (int) (slash - file_name), file_name, slash + 1)
             : xasprintf(".%s.~lock~", file_name));
 }
 
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 c33405f..47d89ef 100644 (file)
 #define ADVERTISED_Asym_Pause           (1 << 14)
 #endif
 
+/* Provider-specific netdev object.  Netdev objects are devices that are
+ * created by the netdev library through a netdev_create() call. */
+struct netdev_obj_linux {
+    struct netdev_obj netdev_obj;
+
+    int tap_fd;                 /* File descriptor for TAP device. */
+};
+
 struct netdev_linux {
     struct netdev netdev;
 
@@ -142,6 +150,13 @@ static int set_etheraddr(const char *netdev_name, int hwaddr_family,
 static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
 static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
 
+static struct netdev_obj_linux *
+netdev_obj_linux_cast(const struct netdev_obj *netdev_obj)
+{
+    netdev_obj_assert_class(netdev_obj, &netdev_linux_class);
+    return CONTAINER_OF(netdev_obj, struct netdev_obj_linux, netdev_obj);
+}
+
 static struct netdev_linux *
 netdev_linux_cast(const struct netdev *netdev)
 {
@@ -194,9 +209,77 @@ netdev_linux_cache_cb(const struct rtnetlink_change *change,
     }
 }
 
+/* Creates the netdev object of 'type' with 'name'. */
+static int
+netdev_linux_create(const char *name, const char *type, 
+                    const struct shash *args, bool created)
+{
+    struct netdev_obj_linux *netdev_obj;
+    static const char tap_dev[] = "/dev/net/tun";
+    struct ifreq ifr;
+    int error;
+
+    if (!shash_is_empty(args)) {
+        VLOG_WARN("arguments for %s devices should be empty", type);
+    }
+
+    /* Create the name binding in the netdev library for this object. */
+    netdev_obj = xcalloc(1, sizeof *netdev_obj);
+    netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_linux_class,
+                    created);
+    netdev_obj->tap_fd = -1;
+
+    if (strcmp(type, "tap")) {
+        return 0;
+    }
+
+    /* Open tap device. */
+    netdev_obj->tap_fd = open(tap_dev, O_RDWR);
+    if (netdev_obj->tap_fd < 0) {
+        error = errno;
+        VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error));
+        goto error;
+    }
+
+    /* Create tap device. */
+    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+    if (ioctl(netdev_obj->tap_fd, TUNSETIFF, &ifr) == -1) {
+        VLOG_WARN("%s: creating tap device failed: %s", name,
+                  strerror(errno));
+        error = errno;
+        goto error;
+    }
+
+    /* Make non-blocking. */
+    error = set_nonblocking(netdev_obj->tap_fd);
+    if (error) {
+        goto error;
+    }
+
+    return 0;
+
+error:
+    netdev_destroy(name);
+    return error;
+}
+
+/* Destroys the netdev object 'netdev_obj_'. */
+static void
+netdev_linux_destroy(struct netdev_obj *netdev_obj_)
+{
+    struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_);
+
+    if (netdev_obj->tap_fd >= 0) {
+        close(netdev_obj->tap_fd);
+    }
+    free(netdev_obj);
+
+    return;
+}
+
 static int
-netdev_linux_open(const char *name, char *suffix, int ethertype,
-                  struct netdev **netdevp)
+netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp)
 {
     struct netdev_linux *netdev;
     enum netdev_flags flags;
@@ -204,10 +287,10 @@ netdev_linux_open(const char *name, char *suffix, int ethertype,
 
     /* Allocate network device. */
     netdev = xzalloc(sizeof *netdev);
-    netdev_init(&netdev->netdev, suffix, &netdev_linux_class);
+    netdev_init(&netdev->netdev, name, &netdev_linux_class);
     netdev->netdev_fd = -1;
     netdev->tap_fd = -1;
-    netdev->cache = shash_find_data(&cache_map, suffix);
+    netdev->cache = shash_find_data(&cache_map, name);
     if (!netdev->cache) {
         if (shash_is_empty(&cache_map)) {
             int error = rtnetlink_notifier_register(
@@ -218,14 +301,14 @@ netdev_linux_open(const char *name, char *suffix, int ethertype,
             }
         }
         netdev->cache = xmalloc(sizeof *netdev->cache);
-        netdev->cache->shash_node = shash_add(&cache_map, suffix,
+        netdev->cache->shash_node = shash_add(&cache_map, name,
                                               netdev->cache);
         netdev->cache->valid = 0;
         netdev->cache->ref_cnt = 0;
     }
     netdev->cache->ref_cnt++;
 
-    if (!strncmp(name, "tap:", 4)) {
+    if (!strcmp(netdev_get_type(&netdev->netdev), "tap")) {
         static const char tap_dev[] = "/dev/net/tun";
         struct ifreq ifr;
 
@@ -239,9 +322,9 @@ netdev_linux_open(const char *name, char *suffix, int ethertype,
 
         /* Create tap device. */
         ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
-        strncpy(ifr.ifr_name, suffix, sizeof ifr.ifr_name);
+        strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
         if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) {
-            VLOG_WARN("%s: creating tap device failed: %s", suffix,
+            VLOG_WARN("%s: creating tap device failed: %s", name,
                       strerror(errno));
             error = errno;
             goto error;
@@ -296,7 +379,7 @@ netdev_linux_open(const char *name, char *suffix, int ethertype,
         if (bind(netdev->netdev_fd,
                  (struct sockaddr *) &sll, sizeof sll) < 0) {
             error = errno;
-            VLOG_ERR("bind to %s failed: %s", suffix, strerror(error));
+            VLOG_ERR("bind to %s failed: %s", name, strerror(error));
             goto error;
         }
 
@@ -548,6 +631,17 @@ netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup)
     return 0;
 }
 
+/* Returns the ifindex of 'netdev', if successful, as a positive number.
+ * On failure, returns a negative errno value. */
+static int
+netdev_linux_get_ifindex(const struct netdev *netdev)
+{
+    int ifindex, error;
+
+    error = get_ifindex(netdev, &ifindex);
+    return error ? -error : ifindex;
+}
+
 static int
 netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier)
 {
@@ -720,8 +814,7 @@ netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
 /* Stores the features supported by 'netdev' into each of '*current',
  * '*advertised', '*supported', and '*peer' that are non-null.  Each value is a
  * bitmap of "enum ofp_port_features" bits, in host byte order.  Returns 0 if
- * successful, otherwise a positive errno value.  On failure, all of the
- * passed-in values are set to 0. */
+ * successful, otherwise a positive errno value. */
 static int
 netdev_linux_get_features(struct netdev *netdev,
                           uint32_t *current, uint32_t *advertised,
@@ -1367,13 +1460,16 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_)
 }
 
 const struct netdev_class netdev_linux_class = {
-    "",                         /* prefix */
-    "linux",                    /* name */
+    "system",                   /* type */
 
     netdev_linux_init,
     netdev_linux_run,
     netdev_linux_wait,
 
+    netdev_linux_create,
+    netdev_linux_destroy,
+    NULL,                       /* reconfigure */
+
     netdev_linux_open,
     netdev_linux_close,
 
@@ -1389,6 +1485,7 @@ const struct netdev_class netdev_linux_class = {
     netdev_linux_set_etheraddr,
     netdev_linux_get_etheraddr,
     netdev_linux_get_mtu,
+    netdev_linux_get_ifindex,
     netdev_linux_get_carrier,
     netdev_linux_get_stats,
 
@@ -1411,13 +1508,16 @@ const struct netdev_class netdev_linux_class = {
 };
 
 const struct netdev_class netdev_tap_class = {
-    "tap",                      /* prefix */
-    "tap",                      /* name */
+    "tap",                      /* type */
 
     netdev_linux_init,
     NULL,                       /* run */
     NULL,                       /* wait */
 
+    netdev_linux_create,
+    netdev_linux_destroy,
+    NULL,                       /* reconfigure */
+
     netdev_linux_open,
     netdev_linux_close,
 
@@ -1433,6 +1533,7 @@ const struct netdev_class netdev_tap_class = {
     netdev_linux_set_etheraddr,
     netdev_linux_get_etheraddr,
     netdev_linux_get_mtu,
+    netdev_linux_get_ifindex,
     netdev_linux_get_carrier,
     netdev_linux_get_stats,
 
index 3bc7fd4..64d227e 100644 (file)
 #include <assert.h>
 #include "netdev.h"
 #include "list.h"
+#include "shash.h"
+
+/* A network device object that was created through the netdev_create()
+ * call.
+ *
+ * This structure should be treated as opaque by network device
+ * implementations. */
+struct netdev_obj {
+    const struct netdev_class *class;
+    int ref_cnt;
+    bool created;                    /* Was netdev_create() called? */
+};
+
+void netdev_obj_init(struct netdev_obj *, const char *name,
+                     const struct netdev_class *, bool created);
+static inline void netdev_obj_assert_class(const struct netdev_obj *netdev_obj,
+                                           const struct netdev_class *class)
+{
+    assert(netdev_obj->class == class);
+}
 
 /* A network device (e.g. an Ethernet device).
  *
@@ -30,6 +50,7 @@
 struct netdev {
     const struct netdev_class *class;
     char *name;                      /* e.g. "eth0" */
+
     enum netdev_flags save_flags;    /* Initial device flags. */
     enum netdev_flags changed_flags; /* Flags that we changed. */
     struct list node;                /* Element in global list. */
@@ -42,6 +63,7 @@ static inline void netdev_assert_class(const struct netdev *netdev,
 {
     assert(netdev->class == class);
 }
+const char *netdev_get_type(const struct netdev *netdev);
 
 /* A network device notifier.
  *
@@ -62,15 +84,13 @@ void netdev_notifier_init(struct netdev_notifier *, struct netdev *,
  * These functions return 0 if successful or a positive errno value on failure,
  * except where otherwise noted. */
 struct netdev_class {
-    /* Prefix for names of netdevs in this class, e.g. "ndunix:".
+    /* Type of netdevs in this class, e.g. "system", "tap", "gre", etc.
      *
-     * One netdev class may have the empty string "" as its prefix, in which
-     * case that netdev class is associated with netdev names that do not
-     * contain a colon. */
-    const char *prefix;
-
-    /* Class name, for use in error messages. */
-    const char *name;
+     * One of the providers should supply a "system" type, since this is
+     * the type assumed when a device name was not bound through the 
+     * netdev_create() call.  The "system" type corresponds to an 
+     * existing network device on the system. */
+    const char *type;
 
     /* Called only once, at program startup.  Returning an error from this
      * function will prevent any network device in this class from being
@@ -88,19 +108,45 @@ struct netdev_class {
      * to be called.  May be null if nothing is needed here. */
     void (*wait)(void);
 
+    /* Attempts to create a network device object of 'type' with 'name'.  
+     * 'type' corresponds to the 'type' field used in the netdev_class
+     * structure.  
+     *
+     * The 'created' flag indicates that the user called netdev_create()
+     * and thus will eventually call netdev_destroy().  If the flag is 
+     * false, then the object was dynamically created based on a call to 
+     * netdev_open() without first calling netdev_create() and will be
+     * automatically destroyed when no more netdevs have 'name' open.  A 
+     * provider implementation should pass this flag to netdev_obj_init(). */
+    int (*create)(const char *name, const char *type, 
+                  const struct shash *args, bool created);
+
+    /* Destroys 'netdev_obj'.  
+     *
+     * Netdev objects maintain a reference count that is incremented on 
+     * netdev_open() and decremented on netdev_close().  If 'netdev_obj' 
+     * has a non-zero reference count, then this function will not be 
+     * called. */
+    void (*destroy)(struct netdev_obj *netdev_obj);
+
+    /* Reconfigures the device object 'netdev_obj' with 'args'. 
+     *
+     * If this netdev class does not support reconfiguring a netdev
+     * object, this may be a null pointer.
+     */
+    int (*reconfigure)(struct netdev_obj *netdev_obj, 
+                       const struct shash *args);
+
     /* Attempts to open a network device.  On success, sets '*netdevp' to the
-     * new network device.  'name' is the full network device name provided by
+     * new network device.  'name' is the network device name provided by
      * the user.  This name is useful for error messages but must not be
      * modified.
      *
-     * 'suffix' is a copy of 'name' following the netdev's 'prefix'.
-     *
      * 'ethertype' may be a 16-bit Ethernet protocol value in host byte order
      * to capture frames of that type received on the device.  It may also be
      * one of the 'enum netdev_pseudo_ethertype' values to receive frames in
      * one of those categories. */
-    int (*open)(const char *name, char *suffix, int ethertype,
-                struct netdev **netdevp);
+    int (*open)(const char *name, int ethertype, struct netdev **netdevp);
 
     /* Closes 'netdev'. */
     void (*close)(struct netdev *netdev);
@@ -164,6 +210,16 @@ struct netdev_class {
      * bytes for Ethernet devices.*/
     int (*get_mtu)(const struct netdev *, int *mtup);
 
+    /* Returns the ifindex of 'netdev', if successful, as a positive number.
+     * On failure, returns a negative errno value.
+     *
+     * The desired semantics of the ifindex value are a combination of those
+     * specified by POSIX for if_nametoindex() and by SNMP for ifIndex.  An
+     * ifindex value should be unique within a host and remain stable at least
+     * until reboot.  SNMP says an ifindex "ranges between 1 and the value of
+     * ifNumber" but many systems do not follow this rule anyhow. */
+    int (*get_ifindex)(const struct netdev *);
+
     /* Sets 'carrier' to true if carrier is active (link light is on) on
      * 'netdev'. */
     int (*get_carrier)(const struct netdev *netdev, bool *carrier);
index 481671f..fb0f98e 100644 (file)
@@ -45,6 +45,9 @@ static const struct netdev_class *netdev_classes[] = {
 };
 static int n_netdev_classes = ARRAY_SIZE(netdev_classes);
 
+/* All created network devices. */
+static struct shash netdev_obj_shash = SHASH_INITIALIZER(&netdev_obj_shash);
+
 /* All open network devices. */
 static struct list netdev_list = LIST_INITIALIZER(&netdev_list);
 
@@ -59,7 +62,8 @@ static int restore_flags(struct netdev *netdev);
  * otherwise a positive errno value.
  *
  * Calling this function is optional.  If not called explicitly, it will
- * automatically be called upon the first attempt to open a network device. */
+ * automatically be called upon the first attempt to open or create a 
+ * network device. */
 int
 netdev_initialize(void)
 {
@@ -78,7 +82,7 @@ netdev_initialize(void)
                     netdev_classes[j++] = class;
                 } else {
                     VLOG_ERR("failed to initialize %s network device "
-                             "class: %s", class->name, strerror(retval));
+                             "class: %s", class->type, strerror(retval));
                     if (!status) {
                         status = retval;
                     }
@@ -124,6 +128,92 @@ netdev_wait(void)
     }
 }
 
+/* Attempts to create a network device object of 'type' with 'name'.  'type' 
+ * corresponds to the 'type' field used in the netdev_class * structure.  
+ * Arguments for creation are provided in 'args', which may be empty or NULL 
+ * if none are needed. */
+int
+netdev_create(const char *name, const char *type, const struct shash *args)
+{
+    struct shash empty_args = SHASH_INITIALIZER(&empty_args);
+    int i;
+
+    netdev_initialize();
+
+    if (!args) {
+        args = &empty_args;
+    }
+
+    if (shash_find(&netdev_obj_shash, name)) {
+        VLOG_WARN("attempted to create a netdev object with bound name: %s",
+                name);
+        return EEXIST;
+    }
+
+    for (i = 0; i < n_netdev_classes; i++) {
+        const struct netdev_class *class = netdev_classes[i];
+        if (!strcmp(type, class->type)) {
+            return class->create(name, type, args, true);
+        }
+    }
+
+    VLOG_WARN("could not create netdev object of unknown type: %s", type);
+
+    return EINVAL;
+}
+
+/* Destroys netdev object 'name'.  Netdev objects maintain a reference count
+ * which is incremented on netdev_open() and decremented on netdev_close().  
+ * If 'name' has a non-zero reference count, it will not destroy the object 
+ * and return EBUSY. */
+int
+netdev_destroy(const char *name)
+{
+    struct shash_node *node;
+    struct netdev_obj *netdev_obj;
+
+    node = shash_find(&netdev_obj_shash, name);
+    if (!node) {
+        return ENODEV;
+    }
+
+    netdev_obj = node->data;
+    if (netdev_obj->ref_cnt != 0) {
+        VLOG_WARN("attempt to destroy open netdev object (%d): %s", 
+                netdev_obj->ref_cnt, name);
+        return EBUSY;
+    }
+
+    shash_delete(&netdev_obj_shash, node);
+    netdev_obj->class->destroy(netdev_obj);
+
+    return 0;
+}
+
+/* Reconfigures the device object 'name' with 'args'.  'args' may be empty 
+ * or NULL if none are needed. */
+int
+netdev_reconfigure(const char *name, const struct shash *args)
+{
+    struct shash empty_args = SHASH_INITIALIZER(&empty_args);
+    struct netdev_obj *netdev_obj;
+
+    if (!args) {
+        args = &empty_args;
+    }
+
+    netdev_obj = shash_find_data(&netdev_obj_shash, name);
+    if (!netdev_obj) {
+        return ENODEV;
+    }
+
+    if (netdev_obj->class->reconfigure) {
+        return netdev_obj->class->reconfigure(netdev_obj, args);
+    }
+
+    return 0;
+}
+
 /* Opens the network device named 'name' (e.g. "eth0") and returns zero if
  * successful, otherwise a positive errno value.  On success, sets '*netdevp'
  * to the new network device, otherwise to null.
@@ -133,37 +223,43 @@ netdev_wait(void)
  * the 'enum netdev_pseudo_ethertype' values to receive frames in one of those
  * categories. */
 int
-netdev_open(const char *name_, int ethertype, struct netdev **netdevp)
+netdev_open(const char *name, int ethertype, struct netdev **netdevp)
 {
-    char *name = xstrdup(name_);
-    char *prefix, *suffix, *colon;
+    struct netdev_obj *netdev_obj;
     struct netdev *netdev = NULL;
     int error;
     int i;
 
     netdev_initialize();
-    colon = strchr(name, ':');
-    if (colon) {
-        *colon = '\0';
-        prefix = name;
-        suffix = colon + 1;
+
+    netdev_obj = shash_find_data(&netdev_obj_shash, name);
+    if (netdev_obj) {
+        error = netdev_obj->class->open(name, ethertype, &netdev);
     } else {
-        prefix = "";
-        suffix = name;
-    }
+        /* Default to "system". */
+        error = EAFNOSUPPORT;
+        for (i = 0; i < n_netdev_classes; i++) {
+            const struct netdev_class *class = netdev_classes[i];
+            if (!strcmp(class->type, "system")) {
+                struct shash empty_args = SHASH_INITIALIZER(&empty_args);
 
-    for (i = 0; i < n_netdev_classes; i++) {
-        const struct netdev_class *class = netdev_classes[i];
-        if (!strcmp(prefix, class->prefix)) {
-            error = class->open(name_, suffix, ethertype, &netdev);
-            goto exit;
+                /* Dynamically create the netdev object, but indicate
+                 * that it should be destroyed when the the last user
+                 * closes its handle. */
+                error = class->create(name, "system", &empty_args, false);
+                if (!error) {
+                    error = class->open(name, ethertype, &netdev);
+                    netdev_obj = shash_find_data(&netdev_obj_shash, name);
+                }
+                break;
+            }
         }
     }
-    error = EAFNOSUPPORT;
+    if (!error) {
+        netdev_obj->ref_cnt++;
+    }
 
-exit:
     *netdevp = error ? NULL : netdev;
-    free(name);
     return error;
 }
 
@@ -172,9 +268,24 @@ void
 netdev_close(struct netdev *netdev)
 {
     if (netdev) {
-        char *name;
+        struct netdev_obj *netdev_obj;
+        char *name = netdev->name;
         int error;
 
+        netdev_obj = shash_find_data(&netdev_obj_shash, name);
+        assert(netdev_obj);
+        if (netdev_obj->ref_cnt > 0) {
+            netdev_obj->ref_cnt--;
+        } else {
+            VLOG_WARN("netdev %s closed too many times", name);
+        }
+
+        /* If the reference count for the netdev object is zero, and it
+         * was dynamically created by netdev_open(), destroy it. */
+        if (!netdev_obj->ref_cnt && !netdev_obj->created) {
+            netdev_destroy(name);
+        }
+
         /* Restore flags that we changed, if any. */
         fatal_signal_block();
         error = restore_flags(netdev);
@@ -182,11 +293,10 @@ netdev_close(struct netdev *netdev)
         fatal_signal_unblock();
         if (error) {
             VLOG_WARN("failed to restore network device flags on %s: %s",
-                      netdev->name, strerror(error));
+                      name, strerror(error));
         }
 
         /* Free. */
-        name = netdev->name;
         netdev->class->close(netdev);
         free(name);
     }
@@ -231,7 +341,7 @@ netdev_enumerate(struct svec *svec)
             int retval = class->enumerate(svec);
             if (retval) {
                 VLOG_WARN("failed to enumerate %s network devices: %s",
-                          class->name, strerror(retval));
+                          class->type, strerror(retval));
                 if (!error) {
                     error = retval;
                 }
@@ -366,6 +476,21 @@ netdev_get_mtu(const struct netdev *netdev, int *mtup)
     return error;
 }
 
+/* Returns the ifindex of 'netdev', if successful, as a positive number.  On
+ * failure, returns a negative errno value.
+ *
+ * The desired semantics of the ifindex value are a combination of those
+ * specified by POSIX for if_nametoindex() and by SNMP for ifIndex.  An ifindex
+ * value should be unique within a host and remain stable at least until
+ * reboot.  SNMP says an ifindex "ranges between 1 and the value of ifNumber"
+ * but many systems do not follow this rule anyhow.
+ */
+int
+netdev_get_ifindex(const struct netdev *netdev)
+{
+    return netdev->class->get_ifindex(netdev);
+}
+
 /* Stores the features supported by 'netdev' into each of '*current',
  * '*advertised', '*supported', and '*peer' that are non-null.  Each value is a
  * bitmap of "enum ofp_port_features" bits, in host byte order.  Returns 0 if
@@ -377,11 +502,27 @@ netdev_get_features(struct netdev *netdev,
                     uint32_t *supported, uint32_t *peer)
 {
     uint32_t dummy[4];
-    return netdev->class->get_features(netdev,
-                                       current ? current : &dummy[0],
-                                       advertised ? advertised : &dummy[1],
-                                       supported ? supported : &dummy[2],
-                                       peer ? peer : &dummy[3]);
+    int error;
+
+    if (!current) {
+        current = &dummy[0];
+    }
+    if (!advertised) {
+        advertised = &dummy[1];
+    }
+    if (!supported) {
+        supported = &dummy[2];
+    }
+    if (!peer) {
+        peer = &dummy[3];
+    }
+
+    error = netdev->class->get_features(netdev, current, advertised, supported,
+                                        peer);
+    if (error) {
+        *current = *advertised = *supported = *peer = 0;
+    }
+    return error;
 }
 
 /* Set the features advertised by 'netdev' to 'advertise'.  Returns 0 if
@@ -677,6 +818,24 @@ exit:
     return netdev;
 }
 \f
+/* Initializes 'netdev_obj' as a netdev object named 'name' of the 
+ * specified 'class'.
+ *
+ * This function adds 'netdev_obj' to a netdev-owned shash, so it is
+ * very important that 'netdev_obj' only be freed after calling
+ * netdev_destroy().  */
+void
+netdev_obj_init(struct netdev_obj *netdev_obj, const char *name,
+                const struct netdev_class *class, bool created)
+{
+    assert(!shash_find(&netdev_obj_shash, name));
+
+    netdev_obj->class = class;
+    netdev_obj->ref_cnt = 0;
+    netdev_obj->created = created;
+    shash_add(&netdev_obj_shash, name, netdev_obj);
+}
+
 /* Initializes 'netdev' as a netdev named 'name' of the specified 'class'.
  *
  * This function adds 'netdev' to a netdev-owned linked list, so it is very
@@ -692,6 +851,14 @@ netdev_init(struct netdev *netdev, const char *name,
     list_push_back(&netdev_list, &netdev->node);
 }
 
+/* Returns the class type of 'netdev'.  
+ *
+ * The caller must not free the returned value. */
+const char *netdev_get_type(const struct netdev *netdev)
+{
+    return netdev->class->type;
+}
+
 /* Initializes 'notifier' as a netdev notifier for 'netdev', for which
  * notification will consist of calling 'cb', with auxiliary data 'aux'. */
 void
index 4a29cf3..b8c7dfb 100644 (file)
@@ -30,6 +30,7 @@
 struct ofpbuf;
 struct in_addr;
 struct in6_addr;
+struct shash;
 struct svec;
 
 enum netdev_flags {
@@ -81,6 +82,11 @@ int netdev_initialize(void);
 void netdev_run(void);
 void netdev_wait(void);
 
+int netdev_create(const char *name, const char *type, 
+                  const struct shash *args);
+int netdev_destroy(const char *name);
+int netdev_reconfigure(const char *name, const struct shash *args);
+
 int netdev_open(const char *name, int ethertype, struct netdev **);
 void netdev_close(struct netdev *);
 
@@ -90,6 +96,7 @@ int netdev_enumerate(struct svec *);
 
 const char *netdev_get_name(const struct netdev *);
 int netdev_get_mtu(const struct netdev *, int *mtup);
+int netdev_get_ifindex(const struct netdev *);
 
 int netdev_recv(struct netdev *, struct ofpbuf *);
 void netdev_recv_wait(struct netdev *);
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 26a17e8..32bbc13 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 e400bb5..e6a6c70 100644 (file)
@@ -291,10 +291,12 @@ guess_netmask(uint32_t ip)
             : htonl(0));                          /* ??? */
 }
 
-/* Opens a non-blocking TCP socket and connects to 'target', which should be a
- * string in the format "<host>[:<port>]".  <host> is required.  If
- * 'default_port' is nonzero then <port> is optional and defaults to
- * 'default_port'.
+/* Opens a non-blocking IPv4 socket of the specified 'style' and connects to
+ * 'target', which should be a string in the format "<host>[:<port>]".  <host>
+ * is required.  If 'default_port' is nonzero then <port> is optional and
+ * defaults to 'default_port'.
+ *
+ * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP).
  *
  * On success, returns 0 (indicating connection complete) or EAGAIN (indicating
  * connection in progress), in which case the new file descriptor is stored
@@ -304,8 +306,8 @@ guess_netmask(uint32_t ip)
  * If 'sinp' is non-null, then on success the target address is stored into
  * '*sinp'. */
 int
-tcp_open_active(const char *target_, uint16_t default_port,
-                struct sockaddr_in *sinp, int *fdp)
+inet_open_active(int style, const char *target_, uint16_t default_port,
+                 struct sockaddr_in *sinp, int *fdp)
 {
     char *target = xstrdup(target_);
     char *save_ptr = NULL;
@@ -343,7 +345,7 @@ tcp_open_active(const char *target_, uint16_t default_port,
     }
 
     /* Create non-blocking socket. */
-    fd = socket(AF_INET, SOCK_STREAM, 0);
+    fd = socket(AF_INET, style, 0);
     if (fd < 0) {
         VLOG_ERR("%s: socket: %s", target_, strerror(errno));
         error = errno;
@@ -380,18 +382,20 @@ exit:
     return error;
 }
 
-/* Opens a non-blocking TCP socket, binds to 'target', and listens for incoming
- * connections.  'target' should be a string in the format "[<port>][:<ip>]".
- * <port> may be omitted if 'default_port' is nonzero, in which case it
- * defaults to 'default_port'.  If <ip> is omitted it defaults to the wildcard
- * IP address.
+/* Opens a non-blocking IPv4 socket of the specified 'style', binds to
+ * 'target', and listens for incoming connections.  'target' should be a string
+ * in the format "[<port>][:<ip>]".  <port> may be omitted if 'default_port' is
+ * nonzero, in which case it defaults to 'default_port'.  If <ip> is omitted it
+ * defaults to the wildcard IP address.
+ *
+ * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP).
  *
- * The socket will have SO_REUSEADDR turned on.
+ * For TCP, the socket will have SO_REUSEADDR turned on.
  *
  * On success, returns a non-negative file descriptor.  On failure, returns a
  * negative errno value. */
 int
-tcp_open_passive(const char *target_, uint16_t default_port)
+inet_open_passive(int style, const char *target_, uint16_t default_port)
 {
     char *target = xstrdup(target_);
     char *string_ptr = target;
@@ -427,7 +431,7 @@ tcp_open_passive(const char *target_, uint16_t default_port)
     }
 
     /* Create non-blocking socket, set SO_REUSEADDR. */
-    fd = socket(AF_INET, SOCK_STREAM, 0);
+    fd = socket(AF_INET, style, 0);
     if (fd < 0) {
         error = errno;
         VLOG_ERR("%s: socket: %s", target_, strerror(error));
@@ -437,7 +441,8 @@ tcp_open_passive(const char *target_, uint16_t default_port)
     if (error) {
         goto exit_close;
     }
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
+    if (style == SOCK_STREAM
+        && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
         error = errno;
         VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", target_, strerror(error));
         goto exit_close;
index febe5e7..4259115 100644 (file)
@@ -34,9 +34,9 @@ int get_unix_name_len(socklen_t sun_len);
 uint32_t guess_netmask(uint32_t ip);
 int get_null_fd(void);
 
-int tcp_open_active(const char *target, uint16_t default_port,
+int inet_open_active(int style, const char *target, uint16_t default_port,
                     struct sockaddr_in *sinp, int *fdp);
-int tcp_open_passive(const char *target, uint16_t default_port);
+int inet_open_passive(int style, const char *target, uint16_t default_port);
 
 int read_fully(int fd, void *, size_t, size_t *bytes_read);
 int write_fully(int fd, const void *, size_t, size_t *bytes_written);
index ecd9686..947be9f 100644 (file)
@@ -74,7 +74,7 @@ tcp_open(const char *name, char *suffix, struct stream **streamp)
     struct sockaddr_in sin;
     int fd, error;
 
-    error = tcp_open_active(suffix, 0, &sin, &fd);
+    error = inet_open_active(SOCK_STREAM, suffix, 0, &sin, &fd);
     if (fd >= 0) {
         return new_tcp_stream(name, fd, error, &sin, streamp);
     } else {
@@ -103,7 +103,7 @@ ptcp_open(const char *name UNUSED, char *suffix, struct pstream **pstreamp)
 {
     int fd;
 
-    fd = tcp_open_passive(suffix, 0);
+    fd = inet_open_passive(SOCK_STREAM, suffix, 0);
     if (fd < 0) {
         return -fd;
     } else {
index 81a36b5..bc3df23 100644 (file)
@@ -372,6 +372,22 @@ svec_join(const struct svec *svec,
     return ds_cstr(&ds);
 }
 
+/* Breaks 's' into tokens at any character in 'delimiters', and appends each
+ * token to 'svec'.  Empty tokens are not added. */
+void
+svec_split(struct svec *svec, const char *s_, const char *delimiters)
+{
+    char *s = xstrdup(s_);
+    char *save_ptr = NULL;
+    char *token;
+
+    for (token = strtok_r(s, delimiters, &save_ptr); token != NULL;
+         token = strtok_r(NULL, delimiters, &save_ptr)) {
+        svec_add(svec, token);
+    }
+    free(s);
+}
+
 const char *
 svec_back(const struct svec *svec)
 {
index ff56197..2a93139 100644 (file)
@@ -53,6 +53,7 @@ void svec_swap(struct svec *a, struct svec *b);
 void svec_print(const struct svec *svec, const char *title);
 void svec_parse_words(struct svec *svec, const char *words);
 bool svec_equal(const struct svec *, const struct svec *);
+void svec_split(struct svec *, const char *s, const char *delimiters);
 char *svec_join(const struct svec *,
                 const char *delimiter, const char *terminator);
 const char *svec_back(const struct svec *);
index 84abdfa..5e42387 100644 (file)
@@ -43,8 +43,8 @@ static struct timeval now;
 /* Time at which to die with SIGALRM (if not TIME_MIN). */
 static time_t deadline = TIME_MIN;
 
-static void setup_timer(void);
-static void setup_signal(int flags);
+static void set_up_timer(void);
+static void set_up_signal(int flags);
 static void sigalrm_handler(int);
 static void refresh_if_ticked(void);
 static time_t time_add(time_t, time_t);
@@ -67,12 +67,12 @@ time_init(void)
     gettimeofday(&now, NULL);
     tick = false;
 
-    setup_signal(SA_RESTART);
-    setup_timer();
+    set_up_signal(SA_RESTART);
+    set_up_timer();
 }
 
 static void
-setup_signal(int flags)
+set_up_signal(int flags)
 {
     struct sigaction sa;
 
@@ -100,7 +100,7 @@ setup_signal(int flags)
 void
 time_disable_restart(void)
 {
-    setup_signal(0);
+    set_up_signal(0);
 }
 
 /* Add SA_RESTART to the flags for SIGALRM, so that any system call that
@@ -109,11 +109,11 @@ time_disable_restart(void)
 void
 time_enable_restart(void)
 {
-    setup_signal(SA_RESTART);
+    set_up_signal(SA_RESTART);
 }
 
 static void
-setup_timer(void)
+set_up_timer(void)
 {
     struct itimerval itimer;
 
@@ -133,7 +133,7 @@ setup_timer(void)
 void
 time_postfork(void)
 {
-    setup_timer();
+    set_up_timer();
 }
 
 /* Forces a refresh of the current time from the kernel.  It is not usually
index 8565e58..6378439 100644 (file)
@@ -170,7 +170,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 2452bce..58c54f8 100644 (file)
@@ -288,7 +288,7 @@ ssl_open(const char *name, char *suffix, struct vconn **vconnp)
         return error;
     }
 
-    error = tcp_open_active(suffix, OFP_SSL_PORT, &sin, &fd);
+    error = inet_open_active(SOCK_STREAM, suffix, OFP_SSL_PORT, &sin, &fd);
     if (fd >= 0) {
         int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
         return new_ssl_vconn(name, fd, CLIENT, state, &sin, vconnp);
@@ -776,7 +776,7 @@ pssl_open(const char *name, char *suffix, struct pvconn **pvconnp)
         return retval;
     }
 
-    fd = tcp_open_passive(suffix, OFP_SSL_PORT);
+    fd = inet_open_passive(SOCK_STREAM, suffix, OFP_SSL_PORT);
     if (fd < 0) {
         return -fd;
     }
index b4e5234..aac7166 100644 (file)
@@ -75,7 +75,7 @@ tcp_open(const char *name, char *suffix, struct vconn **vconnp)
     struct sockaddr_in sin;
     int fd, error;
 
-    error = tcp_open_active(suffix, OFP_TCP_PORT, &sin, &fd);
+    error = inet_open_active(SOCK_STREAM, suffix, OFP_TCP_PORT, &sin, &fd);
     if (fd >= 0) {
         return new_tcp_vconn(name, fd, error, &sin, vconnp);
     } else {
@@ -104,7 +104,7 @@ ptcp_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
 {
     int fd;
 
-    fd = tcp_open_passive(suffix, OFP_TCP_PORT);
+    fd = inet_open_passive(SOCK_STREAM, suffix, OFP_TCP_PORT);
     if (fd < 0) {
         return -fd;
     } else {
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 2f056c8..9a9ea06 100644 (file)
@@ -21,6 +21,7 @@ VLOG_MODULE(bridge)
 VLOG_MODULE(chain)
 VLOG_MODULE(cfg)
 VLOG_MODULE(cfg_mod)
+VLOG_MODULE(collectors)
 VLOG_MODULE(controller)
 VLOG_MODULE(coverage)
 VLOG_MODULE(ctlpath)
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 232d45f..87a0fa6 100644 (file)
@@ -7,6 +7,8 @@
 
 noinst_LIBRARIES += ofproto/libofproto.a
 ofproto_libofproto_a_SOURCES = \
+       ofproto/collectors.c \
+       ofproto/collectors.h \
        ofproto/discovery.c \
        ofproto/discovery.h \
        ofproto/executer.c \
diff --git a/ofproto/collectors.c b/ofproto/collectors.c
new file mode 100644 (file)
index 0000000..f7cb1db
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2008, 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "collectors.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "socket-util.h"
+#include "svec.h"
+#include "util.h"
+
+#define THIS_MODULE VLM_collectors
+#include "vlog.h"
+
+struct collectors {
+    int *fds;                     /* Sockets. */
+    size_t n_fds;                 /* Number of sockets. */
+};
+
+/* Opens the targets specified in 'targets' for sending UDP packets.  This is
+ * useful for e.g. sending NetFlow or sFlow packets.  Returns 0 if successful,
+ * otherwise a positive errno value if opening at least one collector failed.
+ *
+ * Each target in 'targets' should be a string in the format "<host>[:<port>]".
+ * <port> may be omitted if 'default_port' is nonzero, in which case it
+ * defaults to 'default_port'.
+ *
+ * '*collectorsp' is set to a null pointer if no targets were successfully
+ * added, otherwise to a new collectors object if at least one was successfully
+ * added.  Thus, even on a failure return, it is possible that '*collectorsp'
+ * is nonnull, and even on a successful return, it is possible that
+ * '*collectorsp' is null, if 'target's is an empty svec. */
+int
+collectors_create(const struct svec *targets_, uint16_t default_port,
+                  struct collectors **collectorsp)
+{
+    struct collectors *c;
+    struct svec targets;
+    int retval = 0;
+    size_t i;
+
+    svec_clone(&targets, targets_);
+    svec_sort_unique(&targets);
+
+    c = xmalloc(sizeof *c);
+    c->fds = xmalloc(sizeof *c->fds * targets.n);
+    c->n_fds = 0;
+    for (i = 0; i < targets.n; i++) {
+        const char *name = targets.names[i];
+        int error;
+        int fd;
+
+        error = inet_open_active(SOCK_DGRAM, name, default_port, NULL, &fd);
+        if (fd >= 0) {
+            c->fds[c->n_fds++] = fd;
+        } else {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+            VLOG_WARN_RL(&rl, "couldn't open connection to collector %s (%s)",
+                         name, strerror(error));
+            if (!retval) {
+                retval = error;
+            }
+        }
+    }
+    svec_destroy(&targets);
+
+    if (c->n_fds) {
+        *collectorsp = c;
+    } else {
+        collectors_destroy(c);
+        *collectorsp = NULL;
+    }
+
+    return retval;
+}
+
+/* Destroys 'c'. */
+void
+collectors_destroy(struct collectors *c)
+{
+    if (c) {
+        size_t i;
+
+        for (i = 0; i < c->n_fds; i++) {
+            close(c->fds[i]);
+        }
+        free(c->fds);
+        free(c);
+    }
+}
+
+/* Sends the 'n'-byte 'payload' to each of the collectors in 'c'. */
+void
+collectors_send(const struct collectors *c, const void *payload, size_t n)
+{
+    size_t i;
+
+    for (i = 0; i < c->n_fds; i++) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        if (send(c->fds[i], payload, n, 0) == -1) {
+            VLOG_WARN_RL(&rl, "sending to collector failed: %s",
+                         strerror(errno));
+        }
+    }
+}
diff --git a/ofproto/collectors.h b/ofproto/collectors.h
new file mode 100644 (file)
index 0000000..a4abb63
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2009 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COLLECTORS_H
+#define COLLECTORS_H 1
+
+#include <stdint.h>
+#include "svec.h"
+
+struct collectors;
+
+int collectors_create(const struct svec *targets, uint16_t default_port,
+                      struct collectors **);
+void collectors_destroy(struct collectors *);
+
+void collectors_send(const struct collectors *, const void *, size_t);
+
+#endif /* collectors.h */
index 4a2ea83..30c24e6 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..7c77c64 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include "cfg.h"
+#include "collectors.h"
 #include "flow.h"
 #include "netflow.h"
 #include "ofpbuf.h"
@@ -37,6 +38,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.
@@ -93,81 +96,30 @@ struct netflow {
     uint8_t engine_type;          /* Value of engine_type to use. */
     uint8_t engine_id;            /* Value of engine_id to use. */
     long long int boot_time;      /* Time when netflow_create() was called. */
-    int *fds;                     /* Sockets for NetFlow collectors. */
-    size_t n_fds;                 /* Number of Netflow collectors. */
+    struct collectors *collectors; /* NetFlow collectors. */
     bool add_id_to_iface;         /* Put the 7 least signficiant bits of 
                                    * 'engine_id' into the most signficant 
                                    * 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);
-
-static int
-open_collector(char *dst)
-{
-    char *save_ptr = NULL;
-    const char *host_name;
-    const char *port_string;
-    struct sockaddr_in sin;
-    int retval;
-    int fd;
-
-    /* Glibc 2.7 has a bug in strtok_r when compiling with optimization that
-     * can cause segfaults here:
-     * http://sources.redhat.com/bugzilla/show_bug.cgi?id=5614.
-     * Using "::" instead of the obvious ":" works around it. */
-    host_name = strtok_r(dst, ":", &save_ptr);
-    port_string = strtok_r(NULL, ":", &save_ptr);
-    if (!host_name) {
-        ovs_error(0, "%s: bad peer name format", dst);
-        return -EAFNOSUPPORT;
-    }
-    if (!port_string) {
-        ovs_error(0, "%s: bad port format", dst);
-        return -EAFNOSUPPORT;
-    }
-
-    memset(&sin, 0, sizeof sin);
-    sin.sin_family = AF_INET;
-    if (lookup_ip(host_name, &sin.sin_addr)) {
-        return -ENOENT;
-    }
-    sin.sin_port = htons(atoi(port_string));
-
-    fd = socket(AF_INET, SOCK_DGRAM, 0);
-    if (fd < 0) {
-        VLOG_ERR("%s: socket: %s", dst, strerror(errno));
-        return -errno;
-    }
-
-    retval = set_nonblocking(fd);
-    if (retval) {
-        close(fd);
-        return -retval;
-    }
-
-    retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
-    if (retval < 0) {
-        int error = errno;
-        VLOG_ERR("%s: connect: %s", dst, strerror(error));
-        close(fd);
-        return -error;
-    }
-
-    return fd;
-}
-
 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 +148,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 +171,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) {
@@ -230,76 +190,40 @@ netflow_expire(struct netflow *nf, const struct ofexpired *expired)
 void
 netflow_run(struct netflow *nf)
 {
-    size_t i;
-
-    if (!nf->packet.size) {
-        return;
-    }
-
-    for (i = 0; i < nf->n_fds; i++) {
-        if (send(nf->fds[i], nf->packet.data, nf->packet.size, 0) == -1) {
-            VLOG_WARN_RL(&rl, "netflow message send failed: %s",
-                         strerror(errno));
-        }
-    }
-    nf->packet.size = 0;
-}
-
-static void
-clear_collectors(struct netflow *nf)
-{
-    size_t i;
-
-    for (i = 0; i < nf->n_fds; i++) {
-        close(nf->fds[i]);
+    if (nf->packet.size) {
+        collectors_send(nf->collectors, nf->packet.data, nf->packet.size);
+        nf->packet.size = 0;
     }
-    free(nf->fds);
-    nf->fds = NULL;
-    nf->n_fds = 0;
 }
 
 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;
-
-    clear_collectors(nf);
-
-    svec_clone(&collectors, collectors_);
-    svec_sort_unique(&collectors);
-
-    nf->fds = xmalloc(sizeof *nf->fds * collectors.n);
-    for (i = 0; i < collectors.n; i++) {
-        const char *name = collectors.names[i];
-        char *tmpname = xstrdup(name);
-        int fd = open_collector(tmpname);
-        free(tmpname);
-        if (fd >= 0) {
-            nf->fds[nf->n_fds++] = fd;
-        } else {
-            VLOG_WARN("couldn't open connection to collector (%s), "
-                      "ignoring %s\n", strerror(-fd), name);
-            if (!error) {
-                error = -fd;
-            }
-        }
+    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;
+
+    collectors_destroy(nf->collectors);
+    collectors_create(&nf_options->collectors, 0, &nf->collectors);
+
+    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();
     }
 
-    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;
-}
-
 struct netflow *
 netflow_create(void)
 {
@@ -307,8 +231,7 @@ netflow_create(void)
     nf->engine_type = 0;
     nf->engine_id = 0;
     nf->boot_time = time_msec();
-    nf->fds = NULL;
-    nf->n_fds = 0;
+    nf->collectors = NULL;
     nf->add_id_to_iface = false;
     nf->netflow_cnt = 0;
     ofpbuf_init(&nf->packet, 1500);
@@ -320,7 +243,50 @@ netflow_destroy(struct netflow *nf)
 {
     if (nf) {
         ofpbuf_uninit(&nf->packet);
-        clear_collectors(nf);
+        collectors_destroy(nf->collectors);
         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 c618e3c..60e1828 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;
     }
@@ -2685,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);
     }
 }
 
@@ -2714,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));
@@ -2905,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);
     }
 
@@ -2913,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);
         }
@@ -3174,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;
 }
@@ -3239,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)
 {
@@ -3296,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);
@@ -3400,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;
@@ -3427,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 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 07ea684..040f633 100644 (file)
@@ -118,7 +118,24 @@ through the controller and every packet is flooded.
 
 This option is most useful for debugging.  It reduces switching
 performance, so it should not be used in production.
-
+.
+.IP "\fB-w\fR, \fB--wildcard\fR"
+By default, \fBovs\-controller\fR sets up exact-match flows.  This
+option allows it to set up wildcarded flows, which may reduce
+flow-setup latency by causing less traffic to be sent up to the
+controller.
+.IP
+This option has no effect when \fB-n\fR (or \fB--noflow\fR) is in use
+(because the controller does not set up flows in that case).
+.
+.IP "\fB-N\fR, \fB--normal\fR"
+By default, \fBovs\-controller\fR directs packets to a particular port
+or floods them.  This option causes it to direct non-flooded packets
+to the OpenFlow \fBOFPP_NORMAL\fR port.  This allows the switch itself
+to make decisions about packet destinations.  Support for
+\fBOFPP_NORMAL\fR is optional in OpenFlow, so this option may not well
+with some non-Open vSwitch switches.
+.
 .IP "\fB--mute\fR"
 Prevents ovs\-controller from replying to any OpenFlow messages sent
 to it by switches.
index 314da18..bb55c7f 100644 (file)
@@ -53,7 +53,13 @@ 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;
+
+/* -N, --normal: Use "NORMAL" action instead of explicit port? */
+static bool action_normal = false;
+
+/* -w, --wildcard: Set up exact match or wildcard flow entries? */
+static bool exact_flows = true;
 
 /* --max-idle: Maximum idle time, in seconds, before flows expire. */
 static int max_idle = 60;
@@ -201,8 +207,9 @@ static void
 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);
+    sw->lswitch = lswitch_create(sw->rconn, learn_macs, exact_flows,
+                                 set_up_flows ? max_idle : -1,
+                                 action_normal);
 }
 
 static int
@@ -239,6 +246,8 @@ parse_options(int argc, char *argv[])
     static struct option long_options[] = {
         {"hub",         no_argument, 0, 'H'},
         {"noflow",      no_argument, 0, 'n'},
+        {"normal",      no_argument, 0, 'N'},
+        {"wildcard",    no_argument, 0, 'w'},
         {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
         {"mute",        no_argument, 0, OPT_MUTE},
         {"help",        no_argument, 0, 'h'},
@@ -268,13 +277,21 @@ parse_options(int argc, char *argv[])
             break;
 
         case 'n':
-            setup_flows = false;
+            set_up_flows = false;
             break;
 
         case OPT_MUTE:
             mute = true;
             break;
 
+        case 'N':
+            action_normal = true;
+            break;
+
+        case 'w':
+            exact_flows = false;
+            break;
+
         case OPT_MAX_IDLE:
             if (!strcmp(optarg, "permanent")) {
                 max_idle = OFP_FLOW_PERMANENT;
@@ -329,6 +346,8 @@ usage(void)
            "  -H, --hub               act as hub instead of learning switch\n"
            "  -n, --noflow            pass traffic, but don't add flows\n"
            "  --max-idle=SECS         max idle time for new flows\n"
+           "  -N, --normal            use OFPAT_NORMAL action\n"
+           "  -w, --wildcard          use wildcards, not exact-match rules\n"
            "  -h, --help              display this help message\n"
            "  -V, --version           display version information\n");
     exit(EXIT_SUCCESS);
index 08713eb..5e50d72 100644 (file)
@@ -335,9 +335,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 934c274..89af18e 100644 (file)
@@ -556,6 +556,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")) {
@@ -1094,7 +1110,7 @@ do_ping(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..25b222f 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
@@ -411,6 +416,22 @@ Sets the directory searched for remote command execution to
 \fBdirectory\fR.  The default directory is
 \fB@pkgdatadir@/commands\fR.
 
+.SS "Datapath Options"
+.
+.IP "\fB\-\-ports=\fIport\fR[\fB,\fIport\fR...]"
+Ordinarily, \fBovs\-openflowd\fR expects the administrator to create
+the specified \fIdatapath\fR and add ports to it externally with a
+utility such as \fBovs\-dpctl\fR.  However, the userspace switch
+datapath is implemented inside \fBovs\-openflowd\fR itself and does
+not (currently) have any external interface for \fBovs\-dpctl\fR to
+access.  As a stopgap measure, this option specifies one or more ports
+to add to the datapath at \fBovs\-openflowd\fR startup time.  Multiple
+ports may be specified as a comma-separated list or by specifying
+\fB\-\-ports\fR multiple times.
+.IP
+See \fBINSTALL.userspace\fR for more information about userspace
+switching.
+
 .SS "Daemon Options"
 .so lib/daemon.man
 
index 603e258..c275cd6 100644 (file)
@@ -64,6 +64,7 @@ struct ofsettings {
     /* Datapath. */
     uint64_t datapath_id;       /* Datapath ID. */
     const char *dp_name;        /* Name of local datapath. */
+    struct svec ports;          /* Set of ports to add to datapath (if any). */
 
     /* Description strings. */
     const char *mfr_desc;       /* Manufacturer. */
@@ -114,6 +115,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();
@@ -134,6 +136,26 @@ main(int argc, char *argv[])
     VLOG_INFO("Open vSwitch version %s", VERSION BUILDNR);
     VLOG_INFO("OpenFlow protocol version 0x%02x", OFP_VERSION);
 
+    /* Create the datapath and add ports to it, if requested by the user. */
+    if (s.ports.n) {
+        struct dpif *dpif;
+        const char *port;
+        size_t i;
+
+        error = dpif_create_and_open(s.dp_name, &dpif);
+        if (error) {
+            ovs_fatal(error, "could not create datapath");
+        }
+
+        SVEC_FOR_EACH (i, port, &s.ports) {
+            error = dpif_port_add(dpif, port, 0, NULL);
+            if (error) {
+                ovs_fatal(error, "failed to add %s as a port", port);
+            }
+        }
+        dpif_close(dpif);
+    }
+
     /* Start OpenFlow processing. */
     error = ofproto_create(s.dp_name, NULL, NULL, &ofproto);
     if (error) {
@@ -155,6 +177,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 +192,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");
     }
@@ -237,6 +267,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
         OPT_COMMAND_DIR,
         OPT_NETFLOW,
         OPT_MGMT_ID,
+        OPT_PORTS,
         VLOG_OPTION_ENUMS,
         LEAK_CHECKER_OPTION_ENUMS
     };
@@ -266,6 +297,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
         {"command-dir", required_argument, 0, OPT_COMMAND_DIR},
         {"netflow",     required_argument, 0, OPT_NETFLOW},
         {"mgmt-id",     required_argument, 0, OPT_MGMT_ID},
+        {"ports",       required_argument, 0, OPT_PORTS},
         {"verbose",     optional_argument, 0, 'v'},
         {"help",        no_argument, 0, 'h'},
         {"version",     no_argument, 0, 'V'},
@@ -302,6 +334,7 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
     s->command_dir = NULL;
     svec_init(&s->netflow);
     s->mgmt_id = 0;
+    svec_init(&s->ports);
     for (;;) {
         int c;
 
@@ -453,6 +486,10 @@ parse_options(int argc, char *argv[], struct ofsettings *s)
             svec_add(&s->snoops, optarg);
             break;
 
+        case OPT_PORTS:
+            svec_split(&s->ports, optarg, ",");
+            break;
+
         case 'h':
             usage();
 
index 7f6326e..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
@@ -68,30 +68,24 @@ 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.
index cc9f52b..bef868c 100755 (executable)
@@ -29,14 +29,14 @@ 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
 
-SYSLOG = True
+enable_syslog = True
 
 class Error(Exception):
     def __init__(self, msg):
@@ -44,7 +44,7 @@ class Error(Exception):
         self.msg = msg
 
 def log(message):
-    if SYSLOG:
+    if enable_syslog:
         syslog.syslog(message)
 
 # XXX Most of the functions below should be integrated into a
@@ -159,11 +159,10 @@ def do_cfg_save(cfg, file):
 
 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)
@@ -183,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
@@ -348,7 +347,7 @@ 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
@@ -548,29 +547,27 @@ def main():
     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 SYSLOG
-            SYSLOG = False
+            global enable_syslog
+            enable_syslog = False
         elif opt == "--oneline":
             oneline = True
         else:
             raise RuntimeError("unhandled option %s" % opt)
 
-    if SYSLOG:
+    if enable_syslog:
         syslog.openlog("ovs-vsctl")
         log("Called as %s" % ' '.join(sys.argv[1:]))
 
@@ -589,7 +586,7 @@ def main():
             need_lock = True
 
     # Execute commands.
-    cfg = cfg_read(VSWITCHD_CONF, need_lock)
+    cfg = cfg_read(vswitchd_conf, need_lock)
     for command in commands:
         output = run_command(cfg, command)
         if oneline:
@@ -601,7 +598,7 @@ def main():
             for line in output:
                 print line
     if need_lock:
-        cfg_save(cfg, VSWITCHD_CONF)
+        cfg_save(cfg, vswitchd_conf)
     sys.exit(0)
 
 if __name__ == "__main__":
index 516439f..df4169f 100644 (file)
 #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"
 #include "port-array.h"
 #include "proc-net-compat.h"
 #include "process.h"
+#include "shash.h"
 #include "socket-util.h"
 #include "stp.h"
 #include "svec.h"
@@ -148,7 +150,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. */
 
@@ -218,6 +220,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 *);
@@ -369,6 +372,70 @@ bridge_configure_ssl(void)
 }
 #endif
 
+/* Attempt to create the network device 'iface_name' through the netdev
+ * library. */
+static int
+set_up_iface(const char *iface_name, bool create) 
+{
+    const char *type;
+    const char *arg;
+    struct svec arg_svec;
+    struct shash args;
+    int error;
+    size_t i;
+
+    /* If a type is not explicitly declared, then assume it's an existing
+     * "system" device. */
+    type = cfg_get_string(0, "iface.%s.type", iface_name);
+    if (!type || !strcmp(type, "system")) {
+        return 0;
+    }
+
+    svec_init(&arg_svec);
+    cfg_get_subsections(&arg_svec, "iface.%s.args", iface_name);
+
+    shash_init(&args);
+    SVEC_FOR_EACH (i, arg, &arg_svec) {
+        const char *value;
+
+        value = cfg_get_string(0, "iface.%s.args.%s", iface_name, arg);
+        if (value) {
+            shash_add(&args, arg, xstrdup(value));
+        }
+    }
+
+    if (create) {
+        error = netdev_create(iface_name, type, &args);
+    } else {
+        /* xxx Check to make sure that the type hasn't changed. */
+        error = netdev_reconfigure(iface_name, &args);
+    }
+
+    svec_destroy(&arg_svec);
+    shash_destroy(&args);
+
+    return error;
+}
+
+static int
+create_iface(const char *iface_name)
+{
+    return set_up_iface(iface_name, true);
+}
+
+static int
+reconfigure_iface(const char *iface_name)
+{
+    return set_up_iface(iface_name, false);
+}
+
+static void
+destroy_iface(const char *iface_name)
+{
+    netdev_destroy(iface_name);
+}
+
+
 /* iterate_and_prune_ifaces() callback function that opens the network device
  * for 'iface', if it is not already open, and retrieves the interface's MAC
  * address and carrier status. */
@@ -524,6 +591,7 @@ bridge_reconfigure(void)
                              p->devname, dpif_name(br->dpif),
                              strerror(retval));
                 }
+                destroy_iface(p->devname);
             }
         }
         svec_destroy(&want_ifaces);
@@ -544,11 +612,25 @@ bridge_reconfigure(void)
         bridge_get_all_ifaces(br, &want_ifaces);
         svec_diff(&want_ifaces, &cur_ifaces, &add_ifaces, NULL, NULL);
 
+        for (i = 0; i < cur_ifaces.n; i++) {
+            const char *if_name = cur_ifaces.names[i];
+            reconfigure_iface(if_name);
+        }
+
         for (i = 0; i < add_ifaces.n; i++) {
             const char *if_name = add_ifaces.names[i];
             bool internal;
             int error;
 
+            /* Attempt to create the network interface in case it
+             * doesn't exist yet. */
+            error = create_iface(if_name);
+            if (error) {
+                VLOG_WARN("could not create iface %s: %s\n", if_name,
+                        strerror(error));
+                continue;
+            }
+
             /* Add to datapath. */
             internal = iface_is_internal(br, if_name);
             error = dpif_port_add(br->dpif, if_name,
@@ -571,9 +653,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);
@@ -597,36 +677,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
@@ -886,9 +976,7 @@ bridge_wait(void)
             continue;
         }
 
-        if (br->ml) {
-            mac_learning_wait(br->ml);
-        }
+        mac_learning_wait(br->ml);
         bond_wait(br);
         brstp_wait(br);
     }
@@ -901,9 +989,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
@@ -933,6 +1019,7 @@ bridge_unixctl_fdb_show(struct unixctl_conn *conn,
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
     const struct bridge *br;
+    const struct mac_entry *e;
 
     br = bridge_lookup(args);
     if (!br) {
@@ -941,16 +1028,13 @@ bridge_unixctl_fdb_show(struct unixctl_conn *conn,
     }
 
     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);
@@ -967,21 +1051,12 @@ bridge_create(const char *name)
     assert(!bridge_lookup(name));
     br = xzalloc(sizeof *br);
 
-    error = dpif_create(name, &br->dpif);
-    if (error == EEXIST || error == EBUSY) {
-        error = dpif_open(name, &br->dpif);
-        if (error) {
-            VLOG_ERR("datapath %s already exists but cannot be opened: %s",
-                     name, strerror(error));
-            free(br);
-            return NULL;
-        }
-        dpif_flow_flush(br->dpif);
-    } else if (error) {
-        VLOG_ERR("failed to create datapath %s: %s", name, strerror(error));
+    error = dpif_create_and_open(name, &br->dpif);
+    if (error) {
         free(br);
         return NULL;
     }
+    dpif_flow_flush(br->dpif);
 
     error = ofproto_create(name, &bridge_ofhooks, br, &br->ofproto);
     if (error) {
@@ -1093,9 +1168,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);
 
@@ -1479,13 +1552,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
@@ -1535,10 +1626,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;
@@ -1575,6 +1668,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;
@@ -1587,19 +1686,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
@@ -1610,20 +1719,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);
-            }
-        }
     }
 }
 
@@ -1749,7 +1857,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;
@@ -1768,7 +1876,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++;
     }
@@ -1836,14 +1946,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++) {
@@ -1862,14 +1974,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',
@@ -1878,12 +2053,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);
@@ -1911,41 +2087,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
@@ -1987,44 +2131,30 @@ process_flow(struct bridge *br, const flow_t *flow,
          * 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 &&
-            (!packet || !is_bcast_arp_reply(flow, packet))) {
+            !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];
-        } else if (!packet) {
-            /* If we are revalidating but don't have a learning entry then
-             * eject the flow.  Installing a flow that floods packets will
-             * prevent us from seeing future packets and learning properly. */
-            return false;
-        }
+    /* 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
@@ -2034,17 +2164,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
@@ -2086,7 +2209,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_;
 
@@ -2099,7 +2223,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
@@ -2109,17 +2233,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;
             }
         }
@@ -2465,7 +2602,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;
     }
 
@@ -2618,14 +2755,10 @@ bond_unixctl_show(struct unixctl_conn *conn,
                 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;
@@ -3328,7 +3461,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);
@@ -3377,6 +3511,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 01450ed..bd621c1 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 2fcf9d4..c649e92 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.
@@ -91,7 +101,8 @@ add_to_bridge()
 
     $cfg_mod -F /etc/ovs-vswitchd.conf \
         --del-match="bridge.*.port=$vif" \
-        --del-match="vlan.$vif.[!0-9]*" \
+        --del-match="vlan.$vif.trunks=*" \
+        --del-match="vlan.$vif.tag=*" \
         --del-match="port.$vif.[!0-9]*" \
         --add="bridge.$bridge.port=$vif" \
         $vid $vif_details -c 
@@ -127,7 +138,8 @@ remove)
        logger -t scripts-vif "${vif} has been removed"
        $cfg_mod -vANY:console:emer -F /etc/ovs-vswitchd.conf \
            --del-match="bridge.*.port=${vif}" \
-           --del-match="vlan.${vif}.[!0-9]*" \
+           --del-match="vlan.${vif}.trunks=*" \
+           --del-match="vlan.${vif}.tag=*" \
            --del-match="port.${vif}.[!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]
index b0ceb40..7ce8bf7 100755 (executable)
@@ -25,22 +25,20 @@ def get_vif_ref(domid, devid):
 
 # Query XAPI for the information we need using the vif's opaque reference
 def dump_vif_info(domid, devid, vif_ref):
+       vif_info = []
+       session = XenAPI.xapi_local()
+       session.xenapi.login_with_password("root", "")
        try: 
-               session = XenAPI.xapi_local()
-               session.xenapi.login_with_password("root", "")
                vif_rec = session.xenapi.VIF.get_record(vif_ref)
                net_rec = session.xenapi.network.get_record(vif_rec["network"])
-               vm_rec = session.xenapi.VM.get_record(vif_rec["VM"])
+               vm_uuid = session.xenapi.VM.get_uuid(vif_rec["VM"])
 
                # Data to allow vNetManager to associate VIFs with xapi data
-               sys.stdout.write('--add=port.vif%s.%s.net-uuid=%s ' 
-                               % (domid, devid, net_rec["uuid"]))
-               sys.stdout.write('--add=port.vif%s.%s.vif-mac=%s ' 
-                               % (domid, devid, vif_rec["MAC"]))
-               sys.stdout.write('--add=port.vif%s.%s.vif-uuid=%s ' 
-                               % (domid, devid, vif_rec["uuid"]))
-               sys.stdout.write('--add=port.vif%s.%s.vm-uuid=%s ' 
-                               % (domid, devid, vm_rec["uuid"]))
+               add_port = '--add=port.vif%s.%s' % (domid, devid)
+               vif_info.append('%s.net-uuid=%s' % (add_port, net_rec["uuid"]))
+               vif_info.append('%s.vif-mac=%s' % (add_port, vif_rec["MAC"]))
+               vif_info.append('%s.vif-uuid=%s' % (add_port, vif_rec["uuid"]))
+               vif_info.append('%s.vm-uuid=%s' % (add_port, vm_uuid))
 
                # vNetManager needs to know the network UUID(s) associated with
                # each datapath.  Normally interface-reconfigure adds them, but
@@ -52,17 +50,18 @@ def dump_vif_info(domid, devid, vif_ref):
                # There may still be a brief delay between the initial
                # ovs-vswitchd connection to vNetManager and setting this
                # configuration variable, but vNetManager can tolerate that.
-               if len(net_rec['PIFs']) == 0:
+               if not net_rec['PIFs']:
                        key = 'bridge.%s.xs-network-uuids' % net_rec['bridge']
                        value = net_rec['uuid']
-                       sys.stdout.write('--del-match=%s=* ' % key)
-                       sys.stdout.write('--add=%s=%s ' % (key, value))
+                       vif_info.append('--del-match=%s=*' % key)
+                       vif_info.append('--add=%s=%s' % (key, value))
        finally:
                session.xenapi.session.logout()
+       print ' '.join(vif_info)
        
 if __name__ == '__main__':
-       if (len(sys.argv) != 3):
-               sys.stderr.write("ERROR: %s <domid> <devid>\n")
+       if len(sys.argv) != 3:
+               sys.stderr.write("ERROR: %s <domid> <devid>\n" % sys.argv[0])
                sys.exit(1)
 
        domid = sys.argv[1]
@@ -71,7 +70,7 @@ if __name__ == '__main__':
        vif_ref = get_vif_ref(domid, devid)
        if not vif_ref:
                sys.stderr.write("ERROR: Could not find interface vif%s.%s\n" 
-                               % (domid, devid))
+                                % (domid, devid))
                sys.exit(1)
 
        dump_vif_info(domid, devid, vif_ref)