Merge branch 'mainstream'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Tue, 4 Feb 2014 23:02:44 +0000 (00:02 +0100)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Tue, 4 Feb 2014 23:02:44 +0000 (00:02 +0100)
78 files changed:
AUTHORS
BUILD.Windows
COPYING
Makefile.am
build-aux/cccl
configure.ac
datapath/datapath.c
datapath/datapath.h
datapath/flow_netlink.c
datapath/flow_table.c
datapath/vport.h
debian/copyright.in
debian/openvswitch-ipsec.init
include/windows/automake.mk
include/windows/getopt.h [new file with mode: 0644]
include/windows/syslog.h
lib/automake.mk
lib/bfd.c
lib/daemon-windows.c [new file with mode: 0644]
lib/daemon.c
lib/daemon.h
lib/daemon.man
lib/dpif-linux.c
lib/dpif-netdev.c
lib/entropy.c
lib/flow.c
lib/flow.h
lib/getopt_long.c [new file with mode: 0644]
lib/match.c
lib/match.h
lib/meta-flow.c
lib/netdev-bsd.c
lib/netdev-dummy.c
lib/netlink-socket.c
lib/netlink-socket.h
lib/nx-match.c
lib/odp-util.c
lib/odp-util.h
lib/ofp-parse.c
lib/ofp-util.c
lib/packets.c
lib/pcap-file.c
lib/pcap-file.h
lib/service-syn.man [new file with mode: 0644]
lib/service.man [new file with mode: 0644]
lib/socket-util.c
lib/socket-util.h
lib/util.c
lib/util.h
lib/vlog-unixctl.man
lib/vlog.c
lib/vlog.man
m4/ax_check_openssl.m4
m4/openvswitch.m4
manpages.mk
ofproto/ofproto-dpif-upcall.c
ofproto/ofproto-dpif-xlate.c
ofproto/ofproto-dpif-xlate.h
ofproto/ofproto-dpif.c
ofproto/ofproto-dpif.h
ofproto/ofproto.c
ofproto/ofproto.h
ovsdb/ovsdb-server.1.in
ovsdb/ovsdb-server.c
python/ovs/daemon.py
tests/daemon.at
tests/odp.at
tests/ofproto-dpif.at
tests/test-flows.c
utilities/ovs-appctl.8.in
utilities/ovs-dev.py
utilities/ovs-dpctl.c
utilities/ovs-ofctl.c
utilities/ovs-vsctl.c
vswitchd/bridge.c
vswitchd/ovs-vswitchd.8.in
vswitchd/ovs-vswitchd.c
vswitchd/vswitch.xml

diff --git a/AUTHORS b/AUTHORS
index 218fe76..c53e97a 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -16,6 +16,7 @@ Andy Zhou               azhou@nicira.com
 Ansis Atteka            aatteka@nicira.com
 Anupam Chanda           achanda@nicira.com
 Arun Sharma             arun.sharma@calsoftinc.com
+Aryan TaheriMonfared    aryan.taherimonfared@uis.no
 Ben Pfaff               blp@nicira.com
 Brian Kruger            bkruger+ovsdev@gmail.com
 Bruce Davie             bsd@nicira.com
@@ -26,7 +27,9 @@ Chuck Short             zulcss@ubuntu.com
 Damien Millescamps      damien.millescamps@6wind.com
 Dan Carpenter           dan.carpenter@oracle.com
 Dan Wendlandt           dan@nicira.com
+Daniel Hiltgen          daniel@netkine.com
 Daniel Roman            droman@nicira.com
+Daniele Di Proietto     daniele.di.proietto@gmail.com
 Daniele Venturino       daniele.venturino@m3s.it
 Danny Kukawka           danny.kukawka@bisect.de
 David Erickson          derickso@stanford.edu
@@ -65,12 +68,14 @@ Joe Stringer            joestringer@nicira.com
 Jun Nakajima            jun.nakajima@intel.com
 Justin Pettit           jpettit@nicira.com
 Keith Amidon            keith@nicira.com
+Ken Ajiro               ajiro@mxw.nes.nec.co.jp
 Krishna Kondaka         kkondaka@vmware.com
 Kyle Mestery            kmestery@cisco.com
 Leo Alterman            lalterman@nicira.com
 Linda Sun               lsun@vmware.com
 Lorand Jakab            lojakab@cisco.com
 Luca Giraudo            lgiraudo@nicira.com
+Luigi Rizzo             rizzo@iet.unipi.it
 Mark Hamilton           mhamilton@nicira.com
 Martin Casado           casado@nicira.com
 Mehak Mahajan           mmahajan@nicira.com
@@ -233,6 +238,7 @@ Spiro Kourtessis        spiro@vmware.com
 Srini Seetharaman       seethara@stanford.edu
 Stephen Hemminger       shemminger@vyatta.com
 Stephen Finucane        stephen.finucane@intel.com
+Suganya Ramachandran    suganyar@vmware.com
 Takayuki HAMA           t-hama@cb.jp.nec.com
 Teemu Koponen           koponen@nicira.com
 Timothy Chen            tchen@nicira.com
@@ -246,6 +252,7 @@ Yeming Zhao             zhaoyeming@gmail.com
 Ying Chen               yingchen@vmware.com
 Yongqiang Liu           liuyq7809@gmail.com
 ankur dwivedi           ankurengg2003@gmail.com
+chen zhang              3zhangchen9211@gmail.com
 kk yap                  yapkke@stanford.edu
 likunyun                kunyunli@hotmail.com
 rahim entezari          rahim.entezari@gmail.com
index 7012bcf..ea931eb 100644 (file)
@@ -30,9 +30,49 @@ to have the $PATH inside the bash to point to the proper compiler and linker.
 One easy way to achieve this is to get into the "Developer Command prompt for
 visual studio" and through it enter into the bash shell available from msys.
 
+If after the above step, a 'which link' inside MSYS's bash says,
+"/bin/link.exe", rename /bin/link.exe to something else so that the
+Visual studio's linker is used.
+
 * Get the Open vSwitch sources from either cloning the repo using git
 or from a distribution tar ball.
 
-* Run ./boot.sh; ./configure CC=./build-aux/cccl; make
-(cccl is a wrapper script that provides the right options to Visual c++
-'cl' compiler.)
+* If you pulled the sources directly from an Open vSwitch Git tree,
+  run boot.sh in the top source directory:
+
+  % ./boot.sh
+
+* In the top source directory, configure the package by running the
+  configure script.  You should provide some configure options to choose
+  the right compiler, linker, libraries, Open vSwitch component installation
+  directories, etc. For example,
+
+  % ./configure CC=./build-aux/cccl LD="`which link`" LIBS="-lws2_32 ..." \
+    --prefix="C:/openvswitch/usr" --localstatedir="C:/openvswitch/var" \
+    --sysconfdir="C:/openvswitch/etc"
+
+* Run make for the ported executables in the top source directory, e.g.:
+
+  % make utilities/ovs-vsctl.exe ovsdb/ovsdb-server.exe
+
+OpenSSL, Open vSwitch and Visual C++
+------------------------------------
+To get SSL support for Open vSwitch on Windows, do the following:
+
+* Install OpenSSL for Windows as suggested at
+http://www.openssl.org/related/binaries.html.
+The link as of this writing suggests to download it from
+http://slproweb.com/products/Win32OpenSSL.html and the latest version is
+"Win32 OpenSSL v1.0.1f".
+
+Note down the directory where OpenSSL is installed (e.g.: C:/OpenSSL-Win32).
+
+* While configuring the package, specify the OpenSSL directory path.
+For example,
+
+  % ./configure CC=./build-aux/cccl LD="`which link`" LIBS="-lws2_32 ..." \
+  --prefix="C:/openvswitch/usr" --localstatedir="C:/openvswitch/var" \
+  --sysconfdir="C:/openvswitch/etc" --enable-ssl \
+  --with-openssl="C:/OpenSSL-Win32"
+
+* Run make for the ported executables.
diff --git a/COPYING b/COPYING
index 6b28b0e..f5bdb2d 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -22,6 +22,10 @@ Public License, version 2.
 File build-aux/cccl is licensed under the GNU General Public
 License, version 2.
 
+The following files are licensed under the 2-clause BSD license.
+    include/windows/getopt.h
+    lib/getopt_long.c
+
 Files under the xenserver directory are licensed on a file-by-file
 basis.  Refer to each file for details.
 
index c0e2787..8e87557 100644 (file)
@@ -10,11 +10,14 @@ ACLOCAL_AMFLAGS = -I m4
 SUBDIRS = datapath
 
 AM_CPPFLAGS = $(SSL_CFLAGS)
+AM_LDFLAGS = $(SSL_LDFLAGS)
 
 if WIN32
 AM_CPPFLAGS += -I $(top_srcdir)/include/windows
 endif
 
+AM_CPPFLAGS += $(SSL_INCLUDES)
+
 AM_CPPFLAGS += -I $(top_srcdir)/include
 AM_CPPFLAGS += -I $(top_srcdir)/lib
 AM_CPPFLAGS += -I $(top_builddir)/lib
@@ -142,10 +145,9 @@ SUFFIXES += .in
                 -e 's,[@]abs_top_srcdir[@],$(abs_top_srcdir),g' \
             > $@.tmp
        @if head -n 1 $@.tmp | grep '#!' > /dev/null; then \
-           echo chmod +x $@.tmp; \
            chmod +x $@.tmp; \
        fi
-       mv $@.tmp $@
+       $(AM_V_GEN) mv $@.tmp $@
 
 .PHONY: clean-pycov
 clean-pycov:
@@ -263,7 +265,8 @@ manpage-check: $(man_MANS) $(dist_man_MANS) $(noinst_man_MANS)
                if grep warning: $@.tmp; then error=:; fi; \
                rm -f $@.tmp; \
        done; \
-       if $$error; then exit 1; else echo touch $@; touch $@; fi
+       if $$error; then exit 1; else touch $@; fi
+       $(AM_V_GEN) touch -c $@
 CLEANFILES += manpage-check
 endif
 
index f6972d4..0c7f3d9 100644 (file)
@@ -33,10 +33,6 @@ EOF
     exit $1
 }
 
-# Put /usr/bin last in the path, to avoid clashes with MSVC's link
-# Ugly workaround, but should work
-PATH=`echo $PATH | sed -e "s#/usr/bin:##" | sed -e "s#/bin:##"`:/usr/bin
-
 case $MACHTYPE in
     *-msys)
         slash="-"
@@ -95,7 +91,8 @@ EOF
 
     -L*)
         path=`echo "$1" | sed 's/-L//'`
-        linkopt="$linkopt /LIBPATH:$path"
+        linkopt="$linkopt ${slash}LIBPATH:\"$path\""
+        cl_linkopt="${slash}link ${slash}LIBPATH:\"$path\""
         ;;
 
     -l*)
@@ -192,7 +189,7 @@ fi
 
 # choose which opts we built up based on which program will actually run
 if test x$prog = xcl ; then
-    opts=$clopt
+    opts="$clopt $cl_linkopt"
 else
     opts=$linkopt
 fi
index 9b6c69e..19c095e 100644 (file)
@@ -137,4 +137,6 @@ if test "$HAVE_NETLINK" = yes && test "$ESX" = no; then
     AC_DEFINE([LINUX_DATAPATH], [1], [System uses the linux datapath module.])
 fi
 
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
+
 AC_OUTPUT
index c756e2f..5f1b34c 100644 (file)
@@ -862,11 +862,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                        goto err_unlock_ovs;
 
                /* The unmasked key has to be the same for flow updates. */
-               error = -EINVAL;
-               if (!ovs_flow_cmp_unmasked_key(flow, &match)) {
-                       OVS_NLERR("Flow modification message rejected, unmasked key does not match.\n");
+               if (!ovs_flow_cmp_unmasked_key(flow, &match))
                        goto err_unlock_ovs;
-               }
 
                /* Update actions. */
                old_acts = ovsl_dereference(flow->sf_acts);
@@ -1089,6 +1086,7 @@ static size_t ovs_dp_cmd_msg_size(void)
        msgsize += nla_total_size(IFNAMSIZ);
        msgsize += nla_total_size(sizeof(struct ovs_dp_stats));
        msgsize += nla_total_size(sizeof(struct ovs_dp_megaflow_stats));
+       msgsize += nla_total_size(sizeof(u32)); /* OVS_DP_ATTR_USER_FEATURES */
 
        return msgsize;
 }
index b3ae7cd..d81e05c 100644 (file)
@@ -198,7 +198,9 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 portid, u32 seq,
 int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
 void ovs_dp_notify_wq(struct work_struct *work);
 
-#define OVS_NLERR(fmt, ...) \
-       pr_info_once("netlink: " fmt, ##__VA_ARGS__)
-
+#define OVS_NLERR(fmt, ...)                                    \
+do {                                                           \
+       if (net_ratelimit())                                    \
+               pr_info("netlink: " fmt, ##__VA_ARGS__);        \
+} while (0)
 #endif /* datapath.h */
index 9b26528..39fe4bf 100644 (file)
@@ -16,6 +16,8 @@
  * 02110-1301, USA
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include "flow.h"
 #include "datapath.h"
 #include <linux/uaccess.h>
@@ -216,14 +218,14 @@ static bool match_validate(const struct sw_flow_match *match,
        if ((key_attrs & key_expected) != key_expected) {
                /* Key attributes check failed. */
                OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n",
-                               key_attrs, key_expected);
+                               (unsigned long long)key_attrs, (unsigned long long)key_expected);
                return false;
        }
 
        if ((mask_attrs & mask_allowed) != mask_attrs) {
                /* Mask attributes check failed. */
                OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n",
-                               mask_attrs, mask_allowed);
+                               (unsigned long long)mask_attrs, (unsigned long long)mask_allowed);
                return false;
        }
 
@@ -628,8 +630,10 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
 
                if (is_mask && exact_5tuple && *exact_5tuple) {
                        if (ipv6_key->ipv6_proto != 0xff ||
-                           !is_all_set((u8 *)ipv6_key->ipv6_src, sizeof(match->key->ipv6.addr.src)) ||
-                           !is_all_set((u8 *)ipv6_key->ipv6_dst, sizeof(match->key->ipv6.addr.dst)))
+                           !is_all_set((const u8 *)ipv6_key->ipv6_src,
+                                       sizeof(match->key->ipv6.addr.src)) ||
+                           !is_all_set((const u8 *)ipv6_key->ipv6_dst,
+                                       sizeof(match->key->ipv6.addr.dst)))
                                *exact_5tuple = false;
                }
        }
index 2e8557c..20854ac 100644 (file)
@@ -59,8 +59,10 @@ static u16 range_n_bytes(const struct sw_flow_key_range *range)
 void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
                       const struct sw_flow_mask *mask)
 {
-       const long *m = (long *)((u8 *)&mask->key + mask->range.start);
-       const long *s = (long *)((u8 *)src + mask->range.start);
+       const long *m = (const long *)((const u8 *)&mask->key +
+                               mask->range.start);
+       const long *s = (const long *)((const u8 *)src +
+                               mask->range.start);
        long *d = (long *)((u8 *)dst + mask->range.start);
        int i;
 
@@ -167,11 +169,12 @@ void ovs_flow_free(struct sw_flow *flow, bool deferred)
        if (!flow)
                return;
 
-       ASSERT_OVSL();
-
        if (flow->mask) {
                struct sw_flow_mask *mask = flow->mask;
 
+               /* ovs-lock is required to protect mask-refcount and
+                * mask list. */
+               ASSERT_OVSL();
                BUG_ON(!mask->ref_count);
                mask->ref_count--;
 
@@ -382,7 +385,7 @@ int ovs_flow_tbl_flush(struct flow_table *flow_table)
 static u32 flow_hash(const struct sw_flow_key *key, int key_start,
                     int key_end)
 {
-       u32 *hash_key = (u32 *)((u8 *)key + key_start);
+       const u32 *hash_key = (const u32 *)((const u8 *)key + key_start);
        int hash_u32s = (key_end - key_start) >> 2;
 
        /* Make sure number of hash bytes are multiple of u32. */
@@ -404,8 +407,8 @@ static bool cmp_key(const struct sw_flow_key *key1,
                    const struct sw_flow_key *key2,
                    int key_start, int key_end)
 {
-       const long *cp1 = (long *)((u8 *)key1 + key_start);
-       const long *cp2 = (long *)((u8 *)key2 + key_start);
+       const long *cp1 = (const long *)((const u8 *)key1 + key_start);
+       const long *cp2 = (const long *)((const u8 *)key2 + key_start);
        long diffs = 0;
        int i;
 
@@ -520,8 +523,8 @@ static struct sw_flow_mask *mask_alloc(void)
 static bool mask_equal(const struct sw_flow_mask *a,
                       const struct sw_flow_mask *b)
 {
-       u8 *a_ = (u8 *)&a->key + a->range.start;
-       u8 *b_ = (u8 *)&b->key + b->range.start;
+       const u8 *a_ = (const u8 *)&a->key + a->range.start;
+       const u8 *b_ = (const u8 *)&b->key + b->range.start;
 
        return  (a->range.end == b->range.end)
                && (a->range.start == b->range.start)
index 2cf2b18..18b723e 100644 (file)
@@ -173,7 +173,7 @@ void ovs_vport_deferred_free(struct vport *vport);
  */
 static inline void *vport_priv(const struct vport *vport)
 {
-       return (u8 *)vport + ALIGN(sizeof(struct vport), VPORT_ALIGN);
+       return (u8 *)(uintptr_t)vport + ALIGN(sizeof(struct vport), VPORT_ALIGN);
 }
 
 /**
@@ -186,9 +186,9 @@ static inline void *vport_priv(const struct vport *vport)
  * the result of a hash table lookup.  @priv must point to the start of the
  * private data area.
  */
-static inline struct vport *vport_from_priv(const void *priv)
+static inline struct vport *vport_from_priv(void *priv)
 {
-       return (struct vport *)(priv - ALIGN(sizeof(struct vport), VPORT_ALIGN));
+       return (struct vport *)((u8 *)priv - ALIGN(sizeof(struct vport), VPORT_ALIGN));
 }
 
 void ovs_vport_receive(struct vport *, struct sk_buff *,
index 0676387..af6c586 100644 (file)
@@ -14,6 +14,7 @@ Upstream Copyright Holders:
        and authors listed above.
        Copyright (c) 2011 Gaetano Catalli
        Copyright (C) 2000-2003 Geoffrey Wossum (gwossum@acm.org)
+       Copyright (C) 2000 The NetBSD Foundation, Inc.
 
 License:
 
@@ -78,6 +79,11 @@ License:
 
        build-aux/cccl
 
+* The following files are licensed under the 2-clause BSD license.
+
+       lib/getopt_long.c
+       include/windows/getopt.h
+
 * The following components are dual-licensed under the
   GNU General Public License version 2 and the Apache License Version 2.0.
 
index 8e5c7b2..a39dd40 100755 (executable)
@@ -70,11 +70,23 @@ running() {
     return 0
 }
 
+uninstall_mark_rule() {
+    iptables -D INPUT -t mangle $1 -j MARK --set-mark 1/1 || return 0
+}
+
+install_mark_rule() {
+    if ( ! iptables -C INPUT -t mangle $1 -j MARK --set-mark 1/1 2> /dev/null); then
+        iptables -A INPUT -t mangle $1 -j MARK --set-mark 1/1
+    fi
+}
+
 start_server() {
     if [ ! -d /var/run/openvswitch ]; then
         install -d -m 755 -o root -g root /var/run/openvswitch
     fi
 
+    install_mark_rule "-p esp"
+    install_mark_rule "-p udp --dport 4500"
     /usr/share/openvswitch/scripts/ovs-monitor-ipsec \
            --pidfile=$PIDFILE --log-file --detach --monitor \
            unix:/var/run/openvswitch/db.sock
@@ -86,6 +98,8 @@ stop_server() {
     if [ -e $PIDFILE ]; then
         kill `cat $PIDFILE`
     fi
+    uninstall_mark_rule "-p esp"
+    uninstall_mark_rule "-p udp --dport 4500"
 
     return 0
 }
index 07ada1b..2771270 100644 (file)
@@ -6,5 +6,6 @@
 # without warranty of any kind.
 
 noinst_HEADERS += \
-       include/windows/windefs.h \
-       include/windows/syslog.h
+       include/windows/getopt.h \
+       include/windows/syslog.h \
+       include/windows/windefs.h
diff --git a/include/windows/getopt.h b/include/windows/getopt.h
new file mode 100644 (file)
index 0000000..9b526ff
--- /dev/null
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _GETOPT_H_
+#define _GETOPT_H_
+
+#include <unistd.h>
+
+extern char *optarg;
+extern int optind;
+
+/*
+ * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions
+ */
+#define no_argument        0
+#define required_argument  1
+#define optional_argument  2
+
+struct option {
+       /* name of long option */
+       const char *name;
+       /*
+        * one of no_argument, required_argument, and optional_argument:
+        * whether option takes an argument
+        */
+       int has_arg;
+       /* if not NULL, set *flag to val when option found */
+       int *flag;
+       /* if flag not NULL, value to set *flag to; else return value */
+       int val;
+};
+
+int getopt_long(int, char * const *, const char *,
+    const struct option *, int *);
+
+#endif /* !_GETOPT_H_ */
index 484cd10..41267da 100644 (file)
 #define LOG_NDELAY      8       /* don't delay open */
 #define LOG_DAEMON      24      /* system daemons */
 
+#define LOG_LOCAL0      (16<<3) /* reserved for local use */
+#define LOG_LOCAL1      (17<<3) /* reserved for local use */
+#define LOG_LOCAL2      (18<<3) /* reserved for local use */
+#define LOG_LOCAL3      (19<<3) /* reserved for local use */
+#define LOG_LOCAL4      (20<<3) /* reserved for local use */
+#define LOG_LOCAL5      (21<<3) /* reserved for local use */
+#define LOG_LOCAL6      (22<<3) /* reserved for local use */
+#define LOG_LOCAL7      (23<<3) /* reserved for local use */
+
+static inline void
+openlog(const char *ident OVS_UNUSED, int option OVS_UNUSED,
+        int facility OVS_UNUSED)
+{
+}
+
+static inline void
+syslog(int priority OVS_UNUSED, const char *format OVS_UNUSED, ...)
+{
+}
+
 #endif /* syslog.h */
index 7900fa3..ebf3f9d 100644 (file)
@@ -40,7 +40,6 @@ lib_libopenvswitch_la_SOURCES = \
        lib/crc32c.h \
        lib/csum.c \
        lib/csum.h \
-       lib/daemon.c \
        lib/daemon.h \
        lib/dhcp.h \
        lib/dummy.c \
@@ -240,10 +239,16 @@ lib_libopenvswitch_la_SOURCES = \
        lib/vswitch-idl.h \
        lib/vtep-idl.c \
        lib/vtep-idl.h
+
 if WIN32
-lib_libopenvswitch_la_SOURCES += lib/latch-windows.c
+lib_libopenvswitch_la_SOURCES += \
+       lib/daemon-windows.c \
+       lib/getopt_long.c \
+       lib/latch-windows.c
 else
-lib_libopenvswitch_la_SOURCES += lib/latch.c
+lib_libopenvswitch_la_SOURCES += \
+       lib/daemon.c \
+       lib/latch.c
 endif
 
 EXTRA_DIST += \
@@ -337,6 +342,8 @@ MAN_FRAGMENTS += \
        lib/memory-unixctl.man \
        lib/ofp-version.man \
        lib/ovs.tmac \
+       lib/service.man \
+       lib/service-syn.man \
        lib/ssl-bootstrap.man \
        lib/ssl-bootstrap-syn.man \
        lib/ssl-peer-ca-cert.man \
index 347444f..a8c2294 100644 (file)
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -172,6 +172,9 @@ struct bfd {
     uint8_t eth_dst[ETH_ADDR_LEN];/* Ethernet destination address. */
     bool eth_dst_set;             /* 'eth_dst' set through database. */
 
+    ovs_be32 ip_src;              /* IPv4 source address. */
+    ovs_be32 ip_dst;              /* IPv4 destination address. */
+
     uint16_t udp_src;             /* UDP source port. */
 
     /* All timers in milliseconds. */
@@ -217,6 +220,8 @@ static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
 static struct hmap all_bfds__ = HMAP_INITIALIZER(&all_bfds__);
 static struct hmap *const all_bfds OVS_GUARDED_BY(mutex) = &all_bfds__;
 
+static bool bfd_lookup_ip(const char *host_name, struct in_addr *)
+    OVS_REQUIRES(mutex);
 static bool bfd_forwarding__(struct bfd *) OVS_REQUIRES(mutex);
 static bool bfd_in_poll(const struct bfd *) OVS_REQUIRES(mutex);
 static void bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex);
@@ -313,7 +318,8 @@ bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg,
     bool need_poll = false;
     bool cfg_min_rx_changed = false;
     bool cpath_down, forwarding_if_rx;
-    const char *hwaddr;
+    const char *hwaddr, *ip_src, *ip_dst;
+    struct in_addr in_addr;
     uint8_t ea[ETH_ADDR_LEN];
 
     if (ovsthread_once_start(&once)) {
@@ -416,6 +422,20 @@ bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg,
         bfd->eth_dst_set = false;
     }
 
+    ip_src = smap_get(cfg, "bfd_src_ip");
+    if (ip_src && bfd_lookup_ip(ip_src, &in_addr)) {
+        memcpy(&bfd->ip_src, &in_addr, sizeof in_addr);
+    } else {
+        bfd->ip_src = htonl(0xA9FE0100); /* 169.254.1.0. */
+    }
+
+    ip_dst = smap_get(cfg, "bfd_dst_ip");
+    if (ip_dst && bfd_lookup_ip(ip_dst, &in_addr)) {
+        memcpy(&bfd->ip_dst, &in_addr, sizeof in_addr);
+    } else {
+        bfd->ip_dst = htonl(0xA9FE0101); /* 169.254.1.1. */
+    }
+
     forwarding_if_rx = smap_get_bool(cfg, "forwarding_if_rx", false);
     if (bfd->forwarding_if_rx != forwarding_if_rx) {
         bfd->forwarding_if_rx = forwarding_if_rx;
@@ -563,9 +583,8 @@ bfd_put_packet(struct bfd *bfd, struct ofpbuf *p,
     ip->ip_ttl = MAXTTL;
     ip->ip_tos = IPTOS_LOWDELAY | IPTOS_THROUGHPUT;
     ip->ip_proto = IPPROTO_UDP;
-    /* Use link local addresses: */
-    put_16aligned_be32(&ip->ip_src, htonl(0xA9FE0100)); /* 169.254.1.0. */
-    put_16aligned_be32(&ip->ip_dst, htonl(0xA9FE0101)); /* 169.254.1.1. */
+    put_16aligned_be32(&ip->ip_src, bfd->ip_src);
+    put_16aligned_be32(&ip->ip_dst, bfd->ip_dst);
     ip->ip_csum = csum(ip, sizeof *ip);
 
     udp = ofpbuf_put_zeros(p, sizeof *udp);
@@ -856,6 +875,16 @@ bfd_forwarding__(struct bfd *bfd) OVS_REQUIRES(mutex)
 }
 
 /* Helpers. */
+static bool
+bfd_lookup_ip(const char *host_name, struct in_addr *addr)
+{
+    if (!inet_aton(host_name, addr)) {
+        VLOG_ERR_RL(&rl, "\"%s\" is not a valid IP address", host_name);
+        return false;
+    }
+    return true;
+}
+
 static bool
 bfd_in_poll(const struct bfd *bfd) OVS_REQUIRES(mutex)
 {
diff --git a/lib/daemon-windows.c b/lib/daemon-windows.c
new file mode 100644 (file)
index 0000000..60e028b
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2014 Nicira, Inc.
+ *
+ * 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 "daemon.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "poll-loop.h"
+#include "vlog.h"
+
+#pragma comment(lib, "advapi32")
+
+VLOG_DEFINE_THIS_MODULE(daemon);
+
+static bool detach;             /* Was --service specified? */
+static bool detached;           /* Have we already detached? */
+
+/* --service-monitor: Should the service be restarted if it dies
+ * unexpectedly? */
+static bool monitor;
+
+/* Handle to the Services Manager and the created service. */
+static SC_HANDLE manager, service;
+
+/* Handle to the status information structure for the current service. */
+static SERVICE_STATUS_HANDLE hstatus;
+
+/* Hold the service's current status. */
+static SERVICE_STATUS service_status;
+
+/* Handle to an event object used to wakeup from poll_block(). */
+static HANDLE wevent;
+
+/* Hold the arguments sent to the main function. */
+static int sargc;
+static char ***sargvp;
+
+static void check_service(void);
+static void handle_scm_callback(void);
+static void init_service_status(void);
+static void set_config_failure_actions(void);
+
+extern int main(int argc, char *argv[]);
+
+void
+daemon_usage(void)
+{
+    printf(
+        "\nService options:\n"
+        "  --service               run in background as a service.\n"
+        "  --service-monitor       restart the service in case of an "
+                                   "unexpected failure. \n",
+        ovs_rundir(), program_name);
+}
+
+/* Registers the call-back and configures the actions in case of a failure
+ * with the Windows services manager. */
+void
+service_start(int *argcp, char **argvp[])
+{
+    int argc = *argcp;
+    char **argv = *argvp;
+    int i;
+    SERVICE_TABLE_ENTRY service_table[] = {
+        {(LPTSTR)program_name, (LPSERVICE_MAIN_FUNCTION)main},
+        {NULL, NULL}
+    };
+
+    /* 'detached' is 'false' when service_start() is called the first time.
+     * It is 'true', when it is called the second time by the Windows services
+     * manager. */
+    if (detached) {
+        init_service_status();
+
+        wevent = CreateEvent(NULL, TRUE, FALSE, NULL);
+        if (!wevent) {
+            char *msg_buf = ovs_lasterror_to_string();
+            VLOG_FATAL("Failed to create a event (%s).", msg_buf);
+        }
+
+        poll_fd_wait_event(0, wevent, POLLIN);
+
+        /* Register the control handler. This function is called by the service
+         * manager to stop the service. */
+        hstatus = RegisterServiceCtrlHandler(program_name,
+                                         (LPHANDLER_FUNCTION)control_handler);
+        if (!hstatus) {
+            char *msg_buf = ovs_lasterror_to_string();
+            VLOG_FATAL("Failed to register the service control handler (%s).",
+                        msg_buf);
+        }
+
+        if (monitor) {
+            set_config_failure_actions();
+        }
+
+        /* When the service control manager does the call back, it does not
+         * send the same arguments as sent to the main function during the
+         * service start. So, use the arguments passed over during the first
+         * time. */
+        *argcp = sargc;
+        *argvp = *sargvp;
+
+        /* XXX: Windows implementation cannot have a unixctl commands in the
+        * traditional sense of unix domain sockets. If an implementation is
+        * done that involves 'unixctl' vlog commands the following call is
+        * needed to make sure that the unixctl commands for vlog get
+        * registered in a daemon, even before the first log message. */
+        vlog_init();
+
+        return;
+    }
+
+    assert_single_threaded();
+
+    /* A reference to arguments passed to the main function the first time.
+     * We need it after the call-back from service control manager. */
+    sargc = argc;
+    sargvp = argvp;
+
+    /* We are only interested in the '--service' and '--service-monitor'
+     * options before the call-back from the service control manager. */
+    for (i = 0; i < argc; i ++) {
+        if (!strcmp(argv[i], "--service")) {
+            detach = true;
+        } else if (!strcmp(argv[i], "--service-monitor")) {
+            monitor = true;
+        }
+    }
+
+    /* If '--service' is not a command line option, run in foreground. */
+    if (!detach) {
+        return;
+    }
+
+    /* If we have been configured to run as a service, then that service
+     * should already have been created either manually or through a start up
+     * script. */
+    check_service();
+
+    detached = true;
+
+    /* StartServiceCtrlDispatcher blocks and returns after the service is
+     * stopped. */
+    if (!StartServiceCtrlDispatcher(service_table)) {
+        char *msg_buf = ovs_lasterror_to_string();
+        VLOG_FATAL("Failed at StartServiceCtrlDispatcher (%s)", msg_buf);
+    }
+    exit(0);
+}
+
+/* This function is registered with the Windows services manager through
+ * a call to RegisterServiceCtrlHandler() and will be called by the Windows
+ * services manager asynchronously to stop the service. */
+void
+control_handler(DWORD request)
+{
+    switch (request) {
+    case SERVICE_CONTROL_STOP:
+    case SERVICE_CONTROL_SHUTDOWN:
+        service_status.dwCurrentState = SERVICE_STOPPED;
+        service_status.dwWin32ExitCode = NO_ERROR;
+        SetEvent(wevent);
+        break;
+
+    default:
+        break;
+    }
+}
+
+/* Return 'true' if the Windows services manager has called the
+ * control_handler() and asked the program to terminate. */
+bool
+should_service_stop(void)
+{
+    if (detached) {
+        if (service_status.dwCurrentState != SERVICE_RUNNING) {
+            return true;
+        } else {
+            poll_fd_wait_event(0, wevent, POLLIN);
+        }
+    }
+    return false;
+}
+
+/* Set the service as stopped. The control manager will terminate the
+ * service soon after this call. Hence, this should ideally be the last
+ * call before termination. */
+void
+service_stop()
+{
+    ResetEvent(wevent);
+    CloseHandle(wevent);
+
+    service_status.dwCurrentState = SERVICE_STOPPED;
+    service_status.dwWin32ExitCode = NO_ERROR;
+    SetServiceStatus(hstatus, &service_status);
+}
+
+/* Call this function to signal that the daemon is ready. init_service()
+ * or control_handler() has already initalized/set the
+ * service_status.dwCurrentState .*/
+static void
+service_complete(void)
+{
+    if (hstatus) {
+        SetServiceStatus(hstatus, &service_status);
+    }
+}
+
+/* Check whether 'program_name' has been created as a service. */
+static void
+check_service()
+{
+    /* Establish a connection to the local service control manager. */
+    manager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
+    if (!manager) {
+        char *msg_buf = ovs_lasterror_to_string();
+        VLOG_FATAL("Failed to open the service control manager (%s).",
+                   msg_buf);
+    }
+
+    service = OpenService(manager, program_name, SERVICE_ALL_ACCESS);
+    if (!service) {
+        char *msg_buf = ovs_lasterror_to_string();
+        VLOG_FATAL("Failed to open service (%s).", msg_buf);
+    }
+}
+
+/* Service status of a service can be checked asynchronously through
+ * tools like 'sc' or through Windows services manager and is set
+ * through a call to SetServiceStatus(). */
+static void
+init_service_status()
+{
+    /* The service runs in its own process. */
+    service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+
+    /* The control codes the service accepts. */
+    service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+                                            SERVICE_ACCEPT_SHUTDOWN;
+
+    /* Initialize the current state as SERVICE_RUNNING. */
+    service_status.dwCurrentState = SERVICE_RUNNING;
+
+    /* The exit code to indicate if there was an error. */
+    service_status.dwWin32ExitCode = NO_ERROR;
+
+    /* The checkpoint value the service increments periodically. Set as 0
+     * as we do not plan to periodically increment the value. */
+    service_status.dwCheckPoint = 0;
+
+    /* The estimated time required for the stop operation in ms. */
+    service_status.dwWaitHint = 1000;
+}
+
+/* In case of an unexpected termination, configure the action to be
+ * taken. */
+static void
+set_config_failure_actions()
+{
+    /* In case of a failure, restart the process the first two times
+     * After 'dwResetPeriod', the failure count is reset. */
+    SC_ACTION fail_action[3] = {
+        {SC_ACTION_RESTART, 0},
+        {SC_ACTION_RESTART, 0},
+        {SC_ACTION_NONE, 0}
+    };
+    SERVICE_FAILURE_ACTIONS service_fail_action;
+
+    /* Reset failure count after (in seconds). */
+    service_fail_action.dwResetPeriod = 10;
+
+    /* Reboot message. */
+    service_fail_action.lpRebootMsg = NULL;
+
+    /* The command line of the process. */
+    service_fail_action.lpCommand = NULL;
+
+    /* Number of elements in 'fail_actions'. */
+    service_fail_action.cActions = sizeof(fail_action)/sizeof(fail_action[0]);
+
+    /* A pointer to an array of SC_ACTION structures. */
+    service_fail_action.lpsaActions = fail_action;
+
+    if (!ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS,
+                              &service_fail_action)) {
+        char *msg_buf = ovs_lasterror_to_string();
+        VLOG_FATAL("Failed to configure service fail actions (%s).", msg_buf);
+    }
+}
+
+\f
+/* Stub functions to handle daemonize related calls in non-windows platform. */
+bool
+get_detach()
+{
+    return false;
+}
+
+void
+daemon_save_fd(int fd OVS_UNUSED)
+{
+}
+
+void
+daemonize(void)
+{
+}
+
+void daemonize_start(void)
+{
+}
+
+void
+daemonize_complete(void)
+{
+    service_complete();
+}
index 5ed2895..f9290ef 100644 (file)
@@ -69,10 +69,13 @@ static bool save_fds[3];
 
 static void check_already_running(void);
 static int lock_pidfile(FILE *, int command);
+static char *make_pidfile_name(const char *name);
+static pid_t fork_and_clean_up(void);
+static void daemonize_post_detach(void);
 
 /* Returns the file name that would be used for a pidfile if 'name' were
  * provided to set_pidfile().  The caller must free the returned string. */
-char *
+static char *
 make_pidfile_name(const char *name)
 {
     return (!name
@@ -94,15 +97,6 @@ set_pidfile(const char *name)
     pidfile = make_pidfile_name(name);
 }
 
-/* Returns an absolute path to the configured pidfile, or a null pointer if no
- * pidfile is configured.  The caller must not modify or free the returned
- * string. */
-const char *
-get_pidfile(void)
-{
-    return pidfile;
-}
-
 /* Sets that we do not chdir to "/". */
 void
 set_no_chdir(void)
@@ -110,13 +104,6 @@ set_no_chdir(void)
     chdir_ = false;
 }
 
-/* Will we chdir to "/" as part of daemonizing? */
-bool
-is_chdir_enabled(void)
-{
-    return chdir_;
-}
-
 /* Normally, daemonize() or damonize_start() will terminate the program with a
  * message if a locked pidfile already exists.  If this function is called, an
  * existing pidfile will be replaced, with a warning. */
@@ -165,26 +152,6 @@ daemon_save_fd(int fd)
     save_fds[fd] = true;
 }
 
-/* Unregisters pidfile from being unlinked when the program terminates via
-* exit() or a fatal signal. */
-void
-remove_pidfile_from_unlink(void)
-{
-    if (pidfile) {
-        fatal_signal_remove_file_to_unlink(pidfile);
-    }
-}
-
-/* Registers pidfile to be unlinked when the program terminates via exit() or a
- * fatal signal. */
-void
-add_pidfile_to_unlink(void)
-{
-    if (pidfile) {
-        fatal_signal_add_file_to_unlink(pidfile);
-    }
-}
-
 /* If a pidfile has been configured, creates it and stores the running
  * process's pid in it.  Ensures that the pidfile will be deleted when the
  * process exits. */
@@ -280,7 +247,7 @@ daemonize(void)
  * Post-fork, but before returning, this function calls a few other functions
  * that are generally useful if the child isn't planning to exec a new
  * process. */
-pid_t
+static pid_t
 fork_and_clean_up(void)
 {
     pid_t pid = xfork();
@@ -570,7 +537,7 @@ daemonize_complete(void)
  * It only makes sense to call this function as part of an implementation of a
  * special daemon subprocess.  A normal daemon should just call
  * daemonize_complete(). */
-void
+static void
 daemonize_post_detach(void)
 {
     if (detach) {
@@ -750,3 +717,22 @@ check_already_running(void)
                    pidfile, ovs_strerror(-pid));
     }
 }
+
+\f
+/* stub functions for non-windows platform. */
+
+void
+service_start(int *argc OVS_UNUSED, char **argv[] OVS_UNUSED)
+{
+}
+
+void
+service_stop(void)
+{
+}
+
+bool
+should_service_stop(void)
+{
+    return false;
+}
index 14436f3..38ec3ad 100644 (file)
 #include <stdbool.h>
 #include <sys/types.h>
 
+/* This file provides an interface for utilities to run in the background
+ * as daemons on POSIX platforms like Linux or as services on Windows platform.
+ * Some of the functionalities defined in this file are only applicable to
+ * POSIX platforms and some are applicable only on Windows. As such, the
+ * function definitions unique to each platform are separated out with
+ * ifdef macros. More descriptive comments on individual functions are provided
+ * in daemon.c (for Linux) and daemon-windows.c (for Windows).
+
+ * The DAEMON_OPTION_ENUMS, DAEMON_LONG_OPTIONS and DAEMON_OPTION_HANDLERS
+ * macros are useful for parsing command-line options in individual utilities.
+ * For e.g., the command-line option "--detach" is recognized on Linux
+ * and results in calling the set_detach() function. The same option is not
+ * recognized on Windows platform.
+ */
+
+#ifndef _WIN32
 #define DAEMON_OPTION_ENUMS                     \
     OPT_DETACH,                                 \
     OPT_NO_CHDIR,                               \
             daemon_set_monitor();               \
             break;
 
-char *make_pidfile_name(const char *name);
+void set_detach(void);
+void daemon_set_monitor(void);
 void set_pidfile(const char *name);
-const char *get_pidfile(void);
 void set_no_chdir(void);
-bool is_chdir_enabled(void);
-void set_detach(void);
+void ignore_existing_pidfile(void);
+pid_t read_pidfile(const char *name);
+#else
+#define DAEMON_OPTION_ENUMS                     \
+    OPT_SERVICE,                                \
+    OPT_SERVICE_MONITOR
+
+#define DAEMON_LONG_OPTIONS                                             \
+        {"service",            no_argument, NULL, OPT_SERVICE},         \
+        {"service-monitor",    no_argument, NULL, OPT_SERVICE_MONITOR}
+
+#define DAEMON_OPTION_HANDLERS                  \
+        case OPT_SERVICE:                       \
+            break;                              \
+                                                \
+        case OPT_SERVICE_MONITOR:               \
+            break;
+
+void control_handler(DWORD request);
+#endif /* _WIN32 */
+
 bool get_detach(void);
-void daemon_set_monitor(void);
 void daemon_save_fd(int fd);
-void remove_pidfile_from_unlink(void);
-void add_pidfile_to_unlink(void);
 void daemonize(void);
 void daemonize_start(void);
 void daemonize_complete(void);
-void ignore_existing_pidfile(void);
 void daemon_usage(void);
-pid_t read_pidfile(const char *name);
-pid_t read_pidfile_if_exists(const char *name);
-
-pid_t fork_and_clean_up(void);
-void daemonize_post_detach(void);
+void service_start(int *argcp, char **argvp[]);
+void service_stop(void);
+bool should_service_stop(void);
 
 #endif /* daemon.h */
index 00de1a3..4ab9823 100644 (file)
@@ -1,3 +1,4 @@
+The following options are valid on POSIX based platforms.
 .TP
 \fB\-\-pidfile\fR[\fB=\fIpidfile\fR]
 Causes a file (by default, \fB\*(PN.pid\fR) to be created indicating
index 497a5bd..f7f5292 100644 (file)
@@ -25,8 +25,6 @@
 #include <net/if.h>
 #include <linux/types.h>
 #include <linux/pkt_sched.h>
-#include <linux/rtnetlink.h>
-#include <linux/sockios.h>
 #include <poll.h>
 #include <stdlib.h>
 #include <strings.h>
@@ -1014,7 +1012,7 @@ dpif_linux_flow_dump_start(const struct dpif *dpif_, void **statep)
 }
 
 static int
-dpif_linux_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_,
+dpif_linux_flow_dump_next(const struct dpif *dpif_, void *state_,
                           const struct nlattr **key, size_t *key_len,
                           const struct nlattr **mask, size_t *mask_len,
                           const struct nlattr **actions, size_t *actions_len,
index d62949b..b3a3742 100644 (file)
@@ -1041,7 +1041,10 @@ dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
                               struct flow *mask)
 {
     if (mask_key_len) {
-        if (odp_flow_key_to_mask(mask_key, mask_key_len, mask, flow)) {
+        enum odp_key_fitness fitness;
+
+        fitness = odp_flow_key_to_mask(mask_key, mask_key_len, mask, flow);
+        if (fitness) {
             /* This should not happen: it indicates that
              * odp_flow_key_from_mask() and odp_flow_key_to_mask()
              * disagree on the acceptable form of a mask.  Log the problem
@@ -1054,7 +1057,8 @@ dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
                 ds_init(&s);
                 odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s,
                                 true);
-                VLOG_ERR("internal error parsing flow mask %s", ds_cstr(&s));
+                VLOG_ERR("internal error parsing flow mask %s (%s)",
+                         ds_cstr(&s), odp_key_fitness_to_string(fitness));
                 ds_destroy(&s);
             }
 
@@ -1369,7 +1373,8 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
         ofpbuf_use_stack(&buf, &state->maskbuf, sizeof state->maskbuf);
         minimask_expand(&netdev_flow->cr.match.mask, &wc);
         odp_flow_key_from_mask(&buf, &wc.masks, &netdev_flow->flow,
-                               odp_to_u32(wc.masks.in_port.odp_port));
+                               odp_to_u32(wc.masks.in_port.odp_port),
+                               SIZE_MAX);
 
         *mask = buf.data;
         *mask_len = buf.size;
index fd73566..53f7e72 100644 (file)
@@ -56,22 +56,12 @@ get_entropy(void *buffer, size_t n)
 #else
     int error = 0;
     HCRYPTPROV   crypt_prov = 0;
-    LPVOID msg_buf;
 
     CryptAcquireContext(&crypt_prov, NULL, NULL,
                         PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
     if (!CryptGenRandom(crypt_prov, n, buffer)) {
+        char *msg_buf = ovs_lasterror_to_string();
         error = EINVAL;
-        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
-                      | FORMAT_MESSAGE_FROM_SYSTEM
-                      | FORMAT_MESSAGE_IGNORE_INSERTS,
-                      NULL,
-                      GetLastError(),
-                      0,
-                      (LPTSTR)&msg_buf,
-                      0,
-                      NULL
-            );
         VLOG_ERR("CryptGenRandom: read error (%s)", msg_buf);
         LocalFree(msg_buf);
     }
index b1b9f98..06ba036 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -109,12 +109,11 @@ static void
 parse_mpls(struct ofpbuf *b, struct flow *flow)
 {
     struct mpls_hdr *mh;
-    bool top = true;
+    int idx = 0;
 
     while ((mh = ofpbuf_try_pull(b, sizeof *mh))) {
-        if (top) {
-            top = false;
-            flow->mpls_lse = mh->mpls_lse;
+        if (idx < FLOW_MAX_MPLS_LABELS) {
+            flow->mpls_lse[idx++] = mh->mpls_lse;
         }
         if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
             break;
@@ -535,7 +534,7 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc)
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     fmd->tun_id = flow->tunnel.tun_id;
     fmd->tun_src = flow->tunnel.ip_src;
@@ -1047,37 +1046,212 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp)
     flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
 }
 
+/* Returns the number of MPLS LSEs present in 'flow'
+ *
+ * Returns 0 if the 'dl_type' of 'flow' is not an MPLS ethernet type.
+ * Otherwise traverses 'flow''s MPLS label stack stopping at the
+ * first entry that has the BoS bit set. If no such entry exists then
+ * the maximum number of LSEs that can be stored in 'flow' is returned.
+ */
+int
+flow_count_mpls_labels(const struct flow *flow, struct flow_wildcards *wc)
+{
+    if (wc) {
+        wc->masks.dl_type = OVS_BE16_MAX;
+    }
+    if (eth_type_mpls(flow->dl_type)) {
+        int i;
+        int len = FLOW_MAX_MPLS_LABELS;
+
+        for (i = 0; i < len; i++) {
+            if (wc) {
+                wc->masks.mpls_lse[i] |= htonl(MPLS_BOS_MASK);
+            }
+            if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+                return i + 1;
+            }
+        }
+
+        return len;
+    } else {
+        return 0;
+    }
+}
+
+/* Returns the number consecutive of MPLS LSEs, starting at the
+ * innermost LSE, that are common in 'a' and 'b'.
+ *
+ * 'an' must be flow_count_mpls_labels(a).
+ * 'bn' must be flow_count_mpls_labels(b).
+ */
+int
+flow_count_common_mpls_labels(const struct flow *a, int an,
+                              const struct flow *b, int bn,
+                              struct flow_wildcards *wc)
+{
+    int min_n = MIN(an, bn);
+    if (min_n == 0) {
+        return 0;
+    } else {
+        int common_n = 0;
+        int a_last = an - 1;
+        int b_last = bn - 1;
+        int i;
+
+        for (i = 0; i < min_n; i++) {
+            if (wc) {
+                wc->masks.mpls_lse[a_last - i] = OVS_BE32_MAX;
+                wc->masks.mpls_lse[b_last - i] = OVS_BE32_MAX;
+            }
+            if (a->mpls_lse[a_last - i] != b->mpls_lse[b_last - i]) {
+                break;
+            } else {
+                common_n++;
+            }
+        }
+
+        return common_n;
+    }
+}
+
+/* Adds a new outermost MPLS label to 'flow' and changes 'flow''s Ethernet type
+ * to 'mpls_eth_type', which must be an MPLS Ethertype.
+ *
+ * If the new label is the first MPLS label in 'flow', it is generated as;
+ *
+ *     - label: 2, if 'flow' is IPv6, otherwise 0.
+ *
+ *     - TTL: IPv4 or IPv6 TTL, if present and nonzero, otherwise 64.
+ *
+ *     - TC: IPv4 or IPv6 TOS, if present, otherwise 0.
+ *
+ *     - BoS: 1.
+ *
+ * If the new label is the second or label MPLS label in 'flow', it is
+ * generated as;
+ *
+ *     - label: 0.
+ *
+ *     - TTL: Copied from outer label.
+ *
+ *     - TC: Copied from outer label.
+ *
+ *     - BoS: 0.
+ *
+ * 'n' must be flow_count_mpls_labels(flow).  'n' must be less than
+ * FLOW_MAX_MPLS_LABELS (because otherwise flow->mpls_lse[] would overflow).
+ */
+void
+flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
+               struct flow_wildcards *wc)
+{
+    ovs_assert(eth_type_mpls(mpls_eth_type));
+    ovs_assert(n < FLOW_MAX_MPLS_LABELS);
+
+    memset(wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+    if (n) {
+        int i;
+
+        for (i = n; i >= 1; i--) {
+            flow->mpls_lse[i] = flow->mpls_lse[i - 1];
+        }
+        flow->mpls_lse[0] = (flow->mpls_lse[1]
+                             & htonl(MPLS_TTL_MASK | MPLS_TC_MASK));
+    } else {
+        int label = 0;          /* IPv4 Explicit Null. */
+        int tc = 0;
+        int ttl = 64;
+
+        if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+            label = 2;
+        }
+
+        if (is_ip_any(flow)) {
+            tc = (flow->nw_tos & IP_DSCP_MASK) >> 2;
+            wc->masks.nw_tos |= IP_DSCP_MASK;
+
+            if (flow->nw_ttl) {
+                ttl = flow->nw_ttl;
+            }
+            wc->masks.nw_ttl = 0xff;
+        }
+
+        flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
+
+        /* Clear all L3 and L4 fields. */
+        BUILD_ASSERT(FLOW_WC_SEQ == 24);
+        memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
+               sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
+    }
+    flow->dl_type = mpls_eth_type;
+}
+
+/* Tries to remove the outermost MPLS label from 'flow'.  Returns true if
+ * successful, false otherwise.  On success, sets 'flow''s Ethernet type to
+ * 'eth_type'.
+ *
+ * 'n' must be flow_count_mpls_labels(flow). */
+bool
+flow_pop_mpls(struct flow *flow, int n, ovs_be16 eth_type,
+              struct flow_wildcards *wc)
+{
+    int i;
+
+    if (n == 0) {
+        /* Nothing to pop. */
+        return false;
+    } else if (n == FLOW_MAX_MPLS_LABELS
+               && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
+        /* Can't pop because we don't know what to fill in mpls_lse[n - 1]. */
+        return false;
+    }
+
+    memset(wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+    for (i = 1; i < n; i++) {
+        flow->mpls_lse[i - 1] = flow->mpls_lse[i];
+    }
+    flow->mpls_lse[n - 1] = 0;
+    flow->dl_type = eth_type;
+    return true;
+}
+
 /* Sets the MPLS Label that 'flow' matches to 'label', which is interpreted
  * as an OpenFlow 1.1 "mpls_label" value. */
 void
-flow_set_mpls_label(struct flow *flow, ovs_be32 label)
+flow_set_mpls_label(struct flow *flow, int idx, ovs_be32 label)
 {
-    set_mpls_lse_label(&flow->mpls_lse, label);
+    set_mpls_lse_label(&flow->mpls_lse[idx], label);
 }
 
 /* Sets the MPLS TTL that 'flow' matches to 'ttl', which should be in the
  * range 0...255. */
 void
-flow_set_mpls_ttl(struct flow *flow, uint8_t ttl)
+flow_set_mpls_ttl(struct flow *flow, int idx, uint8_t ttl)
 {
-    set_mpls_lse_ttl(&flow->mpls_lse, ttl);
+    set_mpls_lse_ttl(&flow->mpls_lse[idx], ttl);
 }
 
 /* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the
  * range 0...7. */
 void
-flow_set_mpls_tc(struct flow *flow, uint8_t tc)
+flow_set_mpls_tc(struct flow *flow, int idx, uint8_t tc)
 {
-    set_mpls_lse_tc(&flow->mpls_lse, tc);
+    set_mpls_lse_tc(&flow->mpls_lse[idx], tc);
 }
 
 /* Sets the MPLS BOS bit that 'flow' matches to which should be 0 or 1. */
 void
-flow_set_mpls_bos(struct flow *flow, uint8_t bos)
+flow_set_mpls_bos(struct flow *flow, int idx, uint8_t bos)
 {
-    set_mpls_lse_bos(&flow->mpls_lse, bos);
+    set_mpls_lse_bos(&flow->mpls_lse[idx], bos);
 }
 
+/* Sets the entire MPLS LSE. */
+void
+flow_set_mpls_lse(struct flow *flow, int idx, ovs_be32 lse)
+{
+    flow->mpls_lse[idx] = lse;
+}
 
 static void
 flow_compose_l4(struct ofpbuf *b, const struct flow *flow)
@@ -1235,8 +1409,17 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
     }
 
     if (eth_type_mpls(flow->dl_type)) {
+        int n;
+
         b->l2_5 = b->l3;
-        push_mpls(b, flow->dl_type, flow->mpls_lse);
+        for (n = 1; n < FLOW_MAX_MPLS_LABELS; n++) {
+            if (flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK)) {
+                break;
+            }
+        }
+        while (n > 0) {
+            push_mpls(b, flow->dl_type, flow->mpls_lse[--n]);
+        }
     }
 }
 \f
index 9e8549d..3109a84 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ struct ofpbuf;
 /* This sequence number should be incremented whenever anything involving flows
  * or the wildcarding of flows changes.  This will cause build assertion
  * failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 23
+#define FLOW_WC_SEQ 24
 
 #define FLOW_N_REGS 8
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -76,6 +76,9 @@ union flow_in_port {
     odp_port_t odp_port;
 };
 
+/* Maximum number of supported MPLS labels. */
+#define FLOW_MAX_MPLS_LABELS 3
+
 /*
  * A flow in the network.
  *
@@ -106,9 +109,9 @@ struct flow {
     uint8_t dl_dst[6];          /* Ethernet destination address. */
     ovs_be16 dl_type;           /* Ethernet frame type. */
     ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
+    ovs_be32 mpls_lse[FLOW_MAX_MPLS_LABELS]; /* MPLS label stack entry. */
 
     /* L3 */
-    ovs_be32 mpls_lse;          /* MPLS label stack entry. */
     struct in6_addr ipv6_src;   /* IPv6 source address. */
     struct in6_addr ipv6_dst;   /* IPv6 destination address. */
     struct in6_addr nd_target;  /* IPv6 neighbor discovery (ND) target. */
@@ -123,6 +126,7 @@ struct flow {
     uint8_t arp_tha[6];         /* ARP/ND target hardware address. */
     ovs_be16 tcp_flags;         /* TCP flags. With L3 to avoid matching L4. */
     ovs_be16 pad;               /* Padding. */
+
     /* L4 */
     ovs_be16 tp_src;            /* TCP/UDP/SCTP source port. */
     ovs_be16 tp_dst;            /* TCP/UDP/SCTP destination port.
@@ -134,8 +138,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
 BUILD_ASSERT_DECL(offsetof(struct flow, tp_dst) + 2
-                  == sizeof(struct flow_tnl) + 156
-                  && FLOW_WC_SEQ == 23);
+                  == sizeof(struct flow_tnl) + 164
+                  && FLOW_WC_SEQ == 24);
 
 /* Incremental points at which flow classification may be performed in
  * segments.
@@ -145,7 +149,7 @@ BUILD_ASSERT_DECL(offsetof(struct flow, tp_dst) + 2
  * within the struct flow. */
 enum {
     FLOW_SEGMENT_1_ENDS_AT = offsetof(struct flow, dl_src),
-    FLOW_SEGMENT_2_ENDS_AT = offsetof(struct flow, mpls_lse),
+    FLOW_SEGMENT_2_ENDS_AT = offsetof(struct flow, ipv6_src),
     FLOW_SEGMENT_3_ENDS_AT = offsetof(struct flow, tp_src),
 };
 BUILD_ASSERT_DECL(FLOW_SEGMENT_1_ENDS_AT % 4 == 0);
@@ -194,10 +198,19 @@ void flow_set_dl_vlan(struct flow *, ovs_be16 vid);
 void flow_set_vlan_vid(struct flow *, ovs_be16 vid);
 void flow_set_vlan_pcp(struct flow *, uint8_t pcp);
 
-void flow_set_mpls_label(struct flow *flow, ovs_be32 label);
-void flow_set_mpls_ttl(struct flow *flow, uint8_t ttl);
-void flow_set_mpls_tc(struct flow *flow, uint8_t tc);
-void flow_set_mpls_bos(struct flow *flow, uint8_t stack);
+int flow_count_mpls_labels(const struct flow *, struct flow_wildcards *);
+int flow_count_common_mpls_labels(const struct flow *a, int an,
+                                  const struct flow *b, int bn,
+                                  struct flow_wildcards *wc);
+void flow_push_mpls(struct flow *, int n, ovs_be16 mpls_eth_type,
+                    struct flow_wildcards *);
+bool flow_pop_mpls(struct flow *, int n, ovs_be16 eth_type,
+                   struct flow_wildcards *);
+void flow_set_mpls_label(struct flow *, int idx, ovs_be32 label);
+void flow_set_mpls_ttl(struct flow *, int idx, uint8_t ttl);
+void flow_set_mpls_tc(struct flow *, int idx, uint8_t tc);
+void flow_set_mpls_bos(struct flow *, int idx, uint8_t stack);
+void flow_set_mpls_lse(struct flow *, int idx, ovs_be32 lse);
 
 void flow_compose(struct ofpbuf *, const struct flow *);
 
diff --git a/lib/getopt_long.c b/lib/getopt_long.c
new file mode 100644 (file)
index 0000000..a1e7372
--- /dev/null
@@ -0,0 +1,460 @@
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(getopt_long);
+
+int    opterr = 1;             /* if error message should be printed */
+int    optind = 1;             /* index into parent argv vector */
+int    optopt = '?';           /* character checked for validity */
+int    optreset;               /* reset getopt */
+char    *optarg;               /* argument associated with option */
+
+#define IGNORE_FIRST   (*options == '-' || *options == '+')
+#define PRINT_ERROR    ((opterr) && ((*options != ':') \
+                                     || (IGNORE_FIRST && options[1] != ':')))
+#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
+#define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
+/* XXX: GNU ignores PC if *options == '-' */
+#define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
+
+/* return values */
+#define        BADCH   (int)'?'
+#define        BADARG          ((IGNORE_FIRST && options[1] == ':') \
+                        || (*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define        EMSG    ""
+
+#define __UNCONST(a)    ((void *)(unsigned long)(const void *)(a))
+#define _DIAGASSERT(q) ovs_assert(q)
+
+#define warnx VLOG_WARN
+
+static int getopt_internal(int, char **, const char *);
+static int gcd(int, int);
+static void permute_args(int, int, int, char **);
+
+static const char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1;   /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b)
+{
+       int c;
+
+       c = a % b;
+       while (c != 0) {
+               a = b;
+               b = c;
+               c = a % b;
+       }
+
+       return b;
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end, char **nargv)
+{
+       int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+       char *swap;
+
+       _DIAGASSERT(nargv != NULL);
+
+       /*
+        * compute lengths of blocks and number and size of cycles
+        */
+       nnonopts = panonopt_end - panonopt_start;
+       nopts = opt_end - panonopt_end;
+       ncycle = gcd(nnonopts, nopts);
+       cyclelen = (opt_end - panonopt_start) / ncycle;
+
+       for (i = 0; i < ncycle; i++) {
+               cstart = panonopt_end+i;
+               pos = cstart;
+               for (j = 0; j < cyclelen; j++) {
+                       if (pos >= panonopt_end)
+                               pos -= nnonopts;
+                       else
+                               pos += nopts;
+                       swap = nargv[pos];
+                       nargv[pos] = nargv[cstart];
+                       nargv[cstart] = swap;
+               }
+       }
+}
+
+/*
+ * getopt_internal --
+ *     Parse argc/argv argument vector.  Called by user level routines.
+ *  Returns -2 if -- is found (can be long option or end of options marker).
+ */
+static int
+getopt_internal(int nargc, char **nargv, const char *options)
+{
+       char *oli;                              /* option letter list index */
+       int optchar;
+
+       _DIAGASSERT(nargv != NULL);
+       _DIAGASSERT(options != NULL);
+
+       optarg = NULL;
+
+       /*
+        * XXX Some programs (like rsyncd) expect to be able to
+        * XXX re-initialize optind to 0 and have getopt_long(3)
+        * XXX properly function again.  Work around this braindamage.
+        */
+       if (optind == 0)
+               optind = 1;
+
+       if (optreset)
+               nonopt_start = nonopt_end = -1;
+start:
+       if (optreset || !*place) {              /* update scanning pointer */
+               optreset = 0;
+               if (optind >= nargc) {          /* end of argument vector */
+                       place = EMSG;
+                       if (nonopt_end != -1) {
+                               /* do permutation, if we have to */
+                               permute_args(nonopt_start, nonopt_end,
+                                   optind, nargv);
+                               optind -= nonopt_end - nonopt_start;
+                       }
+                       else if (nonopt_start != -1) {
+                               /*
+                                * If we skipped non-options, set optind
+                                * to the first of them.
+                                */
+                               optind = nonopt_start;
+                       }
+                       nonopt_start = nonopt_end = -1;
+                       return -1;
+               }
+               if ((*(place = nargv[optind]) != '-')
+                   || (place[1] == '\0')) {    /* found non-option */
+                       place = EMSG;
+                       if (IN_ORDER) {
+                               /*
+                                * GNU extension:
+                                * return non-option as argument to option 1
+                                */
+                               optarg = nargv[optind++];
+                               return INORDER;
+                       }
+                       if (!PERMUTE) {
+                               /*
+                                * if no permutation wanted, stop parsing
+                                * at first non-option
+                                */
+                               return -1;
+                       }
+                       /* do permutation */
+                       if (nonopt_start == -1)
+                               nonopt_start = optind;
+                       else if (nonopt_end != -1) {
+                               permute_args(nonopt_start, nonopt_end,
+                                   optind, nargv);
+                               nonopt_start = optind -
+                                   (nonopt_end - nonopt_start);
+                               nonopt_end = -1;
+                       }
+                       optind++;
+                       /* process next argument */
+                       goto start;
+               }
+               if (nonopt_start != -1 && nonopt_end == -1)
+                       nonopt_end = optind;
+               if (place[1] && *++place == '-') {      /* found "--" */
+                       place++;
+                       return -2;
+               }
+       }
+       if ((optchar = (int)*place++) == (int)':' ||
+           (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
+               /* option letter unknown or ':' */
+               if (!*place)
+                       ++optind;
+               if (PRINT_ERROR)
+                       warnx(illoptchar, optchar);
+               optopt = optchar;
+               return BADCH;
+       }
+       if (optchar == 'W' && oli[1] == ';') {          /* -W long-option */
+               /* XXX: what if no long options provided (called by getopt)? */
+               if (*place)
+                       return -2;
+
+               if (++optind >= nargc) {        /* no arg */
+                       place = EMSG;
+                       if (PRINT_ERROR)
+                               warnx(recargchar, optchar);
+                       optopt = optchar;
+                       return BADARG;
+               } else                          /* white space */
+                       place = nargv[optind];
+               /*
+                * Handle -W arg the same as --arg (which causes getopt to
+                * stop parsing).
+                */
+               return -2;
+       }
+       if (*++oli != ':') {                    /* doesn't take argument */
+               if (!*place)
+                       ++optind;
+       } else {                                /* takes (optional) argument */
+               optarg = NULL;
+               if (*place)                     /* no white space */
+                       optarg = __UNCONST(place);
+               /* XXX: disable test for :: if PC? (GNU doesn't) */
+               else if (oli[1] != ':') {       /* arg not optional */
+                       if (++optind >= nargc) {        /* no arg */
+                               place = EMSG;
+                               if (PRINT_ERROR)
+                                       warnx(recargchar, optchar);
+                               optopt = optchar;
+                               return BADARG;
+                       } else
+                               optarg = nargv[optind];
+               }
+               place = EMSG;
+               ++optind;
+       }
+       /* dump back option letter */
+       return optchar;
+}
+
+#ifdef REPLACE_GETOPT
+/*
+ * getopt --
+ *     Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the real getopt]
+ */
+int
+getopt(nargc, nargv, options)
+       int nargc;
+       char * const *nargv;
+       const char *options;
+{
+       int retval;
+
+       _DIAGASSERT(nargv != NULL);
+       _DIAGASSERT(options != NULL);
+
+       retval = getopt_internal(nargc, __UNCONST(nargv), options);
+       if (retval == -2) {
+               ++optind;
+               /*
+                * We found an option (--), so if we skipped non-options,
+                * we have to permute.
+                */
+               if (nonopt_end != -1) {
+                       permute_args(nonopt_start, nonopt_end, optind,
+                                      nargv);
+                       optind -= nonopt_end - nonopt_start;
+               }
+               nonopt_start = nonopt_end = -1;
+               retval = -1;
+       }
+       return retval;
+}
+#endif
+
+/*
+ * getopt_long --
+ *     Parse argc/argv argument vector.
+ */
+int
+getopt_long(int nargc, char * const *nargv, const char *options,
+    const struct option *long_options, int *idx)
+{
+       int retval;
+
+#define IDENTICAL_INTERPRETATION(_x, _y)                               \
+       (long_options[(_x)].has_arg == long_options[(_y)].has_arg &&    \
+        long_options[(_x)].flag == long_options[(_y)].flag &&          \
+        long_options[(_x)].val == long_options[(_y)].val)
+
+       _DIAGASSERT(nargv != NULL);
+       _DIAGASSERT(options != NULL);
+       _DIAGASSERT(long_options != NULL);
+       /* idx may be NULL */
+
+       retval = getopt_internal(nargc, __UNCONST(nargv), options);
+       if (retval == -2) {
+               char *current_argv, *has_equal;
+               size_t current_argv_len;
+               int i, ambiguous, match;
+
+               current_argv = __UNCONST(place);
+               match = -1;
+               ambiguous = 0;
+
+               optind++;
+               place = EMSG;
+
+               if (*current_argv == '\0') {            /* found "--" */
+                       /*
+                        * We found an option (--), so if we skipped
+                        * non-options, we have to permute.
+                        */
+                       if (nonopt_end != -1) {
+                               permute_args(nonopt_start, nonopt_end,
+                                   optind, __UNCONST(nargv));
+                               optind -= nonopt_end - nonopt_start;
+                       }
+                       nonopt_start = nonopt_end = -1;
+                       return -1;
+               }
+               if ((has_equal = strchr(current_argv, '=')) != NULL) {
+                       /* argument found (--option=arg) */
+                       current_argv_len = has_equal - current_argv;
+                       has_equal++;
+               } else
+                       current_argv_len = strlen(current_argv);
+
+               for (i = 0; long_options[i].name; i++) {
+                       /* find matching long option */
+                       if (strncmp(current_argv, long_options[i].name,
+                           current_argv_len))
+                               continue;
+
+                       if (strlen(long_options[i].name) ==
+                           (unsigned)current_argv_len) {
+                               /* exact match */
+                               match = i;
+                               ambiguous = 0;
+                               break;
+                       }
+                       if (match == -1)                /* partial match */
+                               match = i;
+                       else if (!IDENTICAL_INTERPRETATION(i, match))
+                               ambiguous = 1;
+               }
+               if (ambiguous) {
+                       /* ambiguous abbreviation */
+                       if (PRINT_ERROR)
+                               warnx(ambig, (int)current_argv_len,
+                                    current_argv);
+                       optopt = 0;
+                       return BADCH;
+               }
+               if (match != -1) {                      /* option found */
+                       if (long_options[match].has_arg == no_argument
+                           && has_equal) {
+                               if (PRINT_ERROR)
+                                       warnx(noarg, (int)current_argv_len,
+                                            current_argv);
+                               /*
+                                * XXX: GNU sets optopt to val regardless of
+                                * flag
+                                */
+                               if (long_options[match].flag == NULL)
+                                       optopt = long_options[match].val;
+                               else
+                                       optopt = 0;
+                               return BADARG;
+                       }
+                       if (long_options[match].has_arg == required_argument ||
+                           long_options[match].has_arg == optional_argument) {
+                               if (has_equal)
+                                       optarg = has_equal;
+                               else if (long_options[match].has_arg ==
+                                   required_argument) {
+                                       /*
+                                        * optional argument doesn't use
+                                        * next nargv
+                                        */
+                                       optarg = nargv[optind++];
+                               }
+                       }
+                       if ((long_options[match].has_arg == required_argument)
+                           && (optarg == NULL)) {
+                               /*
+                                * Missing argument; leading ':'
+                                * indicates no error should be generated
+                                */
+                               if (PRINT_ERROR)
+                                       warnx(recargstring, current_argv);
+                               /*
+                                * XXX: GNU sets optopt to val regardless
+                                * of flag
+                                */
+                               if (long_options[match].flag == NULL)
+                                       optopt = long_options[match].val;
+                               else
+                                       optopt = 0;
+                               --optind;
+                               return BADARG;
+                       }
+               } else {                        /* unknown option */
+                       if (PRINT_ERROR)
+                               warnx(illoptstring, current_argv);
+                       optopt = 0;
+                       return BADCH;
+               }
+               if (long_options[match].flag) {
+                       *long_options[match].flag = long_options[match].val;
+                       retval = 0;
+               } else
+                       retval = long_options[match].val;
+               if (idx)
+                       *idx = match;
+       }
+       return retval;
+#undef IDENTICAL_INTERPRETATION
+}
index cc18a6a..b2a25fd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -95,7 +95,14 @@ match_wc_init(struct match *match, const struct flow *flow)
         memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
         memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
     } else if (eth_type_mpls(flow->dl_type)) {
-        memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+        int i;
+
+        for (i = 0; i < FLOW_MAX_MPLS_LABELS; i++) {
+            wc->masks.mpls_lse[i] = OVS_BE32_MAX;
+            if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+                break;
+            }
+        }
     }
 
     if (flow->dl_type == htons(ETH_TYPE_ARP) ||
@@ -458,55 +465,71 @@ match_set_dl_vlan_pcp(struct match *match, uint8_t dl_vlan_pcp)
     match->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK);
 }
 
+/* Modifies 'match' so that the MPLS label 'idx' matches 'lse' exactly. */
+void
+match_set_mpls_lse(struct match *match, int idx, ovs_be32 lse)
+{
+    match->wc.masks.mpls_lse[idx] = OVS_BE32_MAX;
+    match->flow.mpls_lse[idx] = lse;
+}
+
 /* Modifies 'match' so that the MPLS label is wildcarded. */
 void
-match_set_any_mpls_label(struct match *match)
+match_set_any_mpls_label(struct match *match, int idx)
 {
-    match->wc.masks.mpls_lse &= ~htonl(MPLS_LABEL_MASK);
-    flow_set_mpls_label(&match->flow, htonl(0));
+    match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_LABEL_MASK);
+    flow_set_mpls_label(&match->flow, idx, htonl(0));
 }
 
 /* Modifies 'match' so that it matches only packets with an MPLS header whose
  * label equals the low 20 bits of 'mpls_label'. */
 void
-match_set_mpls_label(struct match *match, ovs_be32 mpls_label)
+match_set_mpls_label(struct match *match, int idx, ovs_be32 mpls_label)
 {
-    match->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK);
-    flow_set_mpls_label(&match->flow, mpls_label);
+    match->wc.masks.mpls_lse[idx] |= htonl(MPLS_LABEL_MASK);
+    flow_set_mpls_label(&match->flow, idx, mpls_label);
 }
 
 /* Modifies 'match' so that the MPLS TC is wildcarded. */
 void
-match_set_any_mpls_tc(struct match *match)
+match_set_any_mpls_tc(struct match *match, int idx)
 {
-    match->wc.masks.mpls_lse &= ~htonl(MPLS_TC_MASK);
-    flow_set_mpls_tc(&match->flow, 0);
+    match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_TC_MASK);
+    flow_set_mpls_tc(&match->flow, idx, 0);
 }
 
 /* Modifies 'match' so that it matches only packets with an MPLS header whose
  * Traffic Class equals the low 3 bits of 'mpls_tc'. */
 void
-match_set_mpls_tc(struct match *match, uint8_t mpls_tc)
+match_set_mpls_tc(struct match *match, int idx, uint8_t mpls_tc)
 {
-    match->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK);
-    flow_set_mpls_tc(&match->flow, mpls_tc);
+    match->wc.masks.mpls_lse[idx] |= htonl(MPLS_TC_MASK);
+    flow_set_mpls_tc(&match->flow, idx, mpls_tc);
 }
 
 /* Modifies 'match' so that the MPLS stack flag is wildcarded. */
 void
-match_set_any_mpls_bos(struct match *match)
+match_set_any_mpls_bos(struct match *match, int idx)
 {
-    match->wc.masks.mpls_lse &= ~htonl(MPLS_BOS_MASK);
-    flow_set_mpls_bos(&match->flow, 0);
+    match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_BOS_MASK);
+    flow_set_mpls_bos(&match->flow, idx, 0);
 }
 
 /* Modifies 'match' so that it matches only packets with an MPLS header whose
  * Stack Flag equals the lower bit of 'mpls_bos' */
 void
-match_set_mpls_bos(struct match *match, uint8_t mpls_bos)
+match_set_mpls_bos(struct match *match, int idx, uint8_t mpls_bos)
+{
+    match->wc.masks.mpls_lse[idx] |= htonl(MPLS_BOS_MASK);
+    flow_set_mpls_bos(&match->flow, idx, mpls_bos);
+}
+
+/* Modifies 'match' so that the MPLS LSE is wildcarded. */
+void
+match_set_any_mpls_lse(struct match *match, int idx)
 {
-    match->wc.masks.mpls_lse |= htonl(MPLS_BOS_MASK);
-    flow_set_mpls_bos(&match->flow, mpls_bos);
+    match->wc.masks.mpls_lse[idx] = htonl(0);
+    flow_set_mpls_lse(&match->flow, idx, htonl(0));
 }
 
 void
@@ -795,6 +818,22 @@ format_be16_masked(struct ds *s, const char *name,
     }
 }
 
+static void
+format_be32_masked(struct ds *s, const char *name,
+                   ovs_be32 value, ovs_be32 mask)
+{
+    if (mask != htonl(0)) {
+        ds_put_format(s, "%s=", name);
+        if (mask == OVS_BE32_MAX) {
+            ds_put_format(s, "%"PRIu32, ntohl(value));
+        } else {
+            ds_put_format(s, "0x%"PRIx32"/0x%"PRIx32,
+                          ntohl(value), ntohl(mask));
+        }
+        ds_put_char(s, ',');
+    }
+}
+
 static void
 format_uint32_masked(struct ds *s, const char *name,
                    uint32_t value, uint32_t mask)
@@ -856,7 +895,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
 
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     if (priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%u,", priority);
@@ -1008,22 +1047,25 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
     if (wc->masks.nw_ttl) {
         ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl);
     }
-    if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+    if (wc->masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK)) {
         ds_put_format(s, "mpls_label=%"PRIu32",",
-                 mpls_lse_to_label(f->mpls_lse));
+                 mpls_lse_to_label(f->mpls_lse[0]));
     }
-    if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) {
+    if (wc->masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) {
         ds_put_format(s, "mpls_tc=%"PRIu8",",
-                 mpls_lse_to_tc(f->mpls_lse));
+                 mpls_lse_to_tc(f->mpls_lse[0]));
     }
-    if (wc->masks.mpls_lse & htonl(MPLS_TTL_MASK)) {
+    if (wc->masks.mpls_lse[0] & htonl(MPLS_TTL_MASK)) {
         ds_put_format(s, "mpls_ttl=%"PRIu8",",
-                 mpls_lse_to_ttl(f->mpls_lse));
+                 mpls_lse_to_ttl(f->mpls_lse[0]));
     }
-    if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
+    if (wc->masks.mpls_lse[0] & htonl(MPLS_BOS_MASK)) {
         ds_put_format(s, "mpls_bos=%"PRIu8",",
-                 mpls_lse_to_bos(f->mpls_lse));
+                 mpls_lse_to_bos(f->mpls_lse[0]));
     }
+    format_be32_masked(s, "mpls_lse1", f->mpls_lse[1], wc->masks.mpls_lse[1]);
+    format_be32_masked(s, "mpls_lse2", f->mpls_lse[2], wc->masks.mpls_lse[2]);
+
     switch (wc->masks.nw_frag) {
     case FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER:
         ds_put_format(s, "nw_frag=%s,",
index ee01acd..7a8ae68 100644 (file)
@@ -78,13 +78,16 @@ void match_set_vlan_vid(struct match *, ovs_be16);
 void match_set_vlan_vid_masked(struct match *, ovs_be16 vid, ovs_be16 mask);
 void match_set_any_pcp(struct match *);
 void match_set_dl_vlan_pcp(struct match *, uint8_t);
-void match_set_any_mpls_label(struct match *);
-void match_set_mpls_label(struct match *, ovs_be32);
-void match_set_any_mpls_tc(struct match *);
-void match_set_mpls_tc(struct match *, uint8_t);
-void match_set_any_mpls_bos(struct match *);
-void match_set_mpls_bos(struct match *, uint8_t);
+void match_set_any_mpls_lse(struct match *, int idx);
+void match_set_mpls_lse(struct match *, int idx, ovs_be32);
+void match_set_any_mpls_label(struct match *, int idx);
+void match_set_mpls_label(struct match *, int idx, ovs_be32);
+void match_set_any_mpls_tc(struct match *, int idx);
+void match_set_mpls_tc(struct match *, int idx, uint8_t);
+void match_set_any_mpls_bos(struct match *, int idx);
+void match_set_mpls_bos(struct match *, int idx, uint8_t);
 void match_set_tp_src(struct match *, ovs_be16);
+void match_set_mpls_lse(struct match *, int idx, ovs_be32 lse);
 void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask);
 void match_set_tp_dst(struct match *, ovs_be16);
 void match_set_tp_dst_masked(struct match *, ovs_be16 port, ovs_be16 mask);
index 96e0efe..a168222 100644 (file)
@@ -926,11 +926,11 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
         return !(wc->masks.vlan_tci & htons(VLAN_PCP_MASK));
 
     case MFF_MPLS_LABEL:
-        return !(wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK));
+        return !(wc->masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK));
     case MFF_MPLS_TC:
-        return !(wc->masks.mpls_lse & htonl(MPLS_TC_MASK));
+        return !(wc->masks.mpls_lse[1] & htonl(MPLS_TC_MASK));
     case MFF_MPLS_BOS:
-        return !(wc->masks.mpls_lse & htonl(MPLS_BOS_MASK));
+        return !(wc->masks.mpls_lse[2] & htonl(MPLS_BOS_MASK));
 
     case MFF_IPV4_SRC:
         return !wc->masks.nw_src;
@@ -1302,15 +1302,16 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         break;
 
     case MFF_MPLS_LABEL:
-        value->be32 = htonl(mpls_lse_to_label(flow->mpls_lse));
+        value->be32 = htonl(mpls_lse_to_label(flow->mpls_lse[0]));
         break;
 
     case MFF_MPLS_TC:
-        value->u8 = mpls_lse_to_tc(flow->mpls_lse);
+        value->u8 = mpls_lse_to_tc(flow->mpls_lse[0]);
         break;
 
     case MFF_MPLS_BOS:
-        value->u8 = mpls_lse_to_bos(flow->mpls_lse);
+        value->u8 = mpls_lse_to_bos(flow->mpls_lse[0]);
+        break;
         break;
 
     case MFF_IPV4_SRC:
@@ -1498,15 +1499,16 @@ mf_set_value(const struct mf_field *mf,
         break;
 
     case MFF_MPLS_LABEL:
-        match_set_mpls_label(match, value->be32);
+        match_set_mpls_label(match, 0, value->be32);
         break;
 
     case MFF_MPLS_TC:
-        match_set_mpls_tc(match, value->u8);
+        match_set_mpls_tc(match, 0, value->u8);
         break;
 
     case MFF_MPLS_BOS:
-        match_set_mpls_bos(match, value->u8);
+        match_set_mpls_bos(match, 0, value->u8);
+        break;
         break;
 
     case MFF_IPV4_SRC:
@@ -1711,15 +1713,16 @@ mf_set_flow_value(const struct mf_field *mf,
         break;
 
     case MFF_MPLS_LABEL:
-        flow_set_mpls_label(flow, value->be32);
+        flow_set_mpls_label(flow, 0, value->be32);
         break;
 
     case MFF_MPLS_TC:
-        flow_set_mpls_tc(flow, value->u8);
+        flow_set_mpls_tc(flow, 0, value->u8);
         break;
 
     case MFF_MPLS_BOS:
-        flow_set_mpls_bos(flow, value->u8);
+        flow_set_mpls_bos(flow, 0, value->u8);
+        break;
         break;
 
     case MFF_IPV4_SRC:
@@ -1921,15 +1924,16 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
         break;
 
     case MFF_MPLS_LABEL:
-        match_set_any_mpls_label(match);
+        match_set_any_mpls_label(match, 0);
         break;
 
     case MFF_MPLS_TC:
-        match_set_any_mpls_tc(match);
+        match_set_any_mpls_tc(match, 0);
         break;
 
     case MFF_MPLS_BOS:
-        match_set_any_mpls_bos(match);
+        match_set_any_mpls_bos(match, 0);
+        break;
         break;
 
     case MFF_IPV4_SRC:
index 689014b..97291bd 100644 (file)
@@ -1460,132 +1460,85 @@ netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off,
     return error;
 }
 
+/* Linux has also different GET_STATS, SET_STATS,
+ * GET_STATUS)
+ */
+#define NETDEV_BSD_CLASS(NAME, CONSTRUCT,            \
+                         GET_FEATURES)               \
+{                                                    \
+    NAME,                                            \
+                                                     \
+    NULL, /* init */                                 \
+    netdev_bsd_run,                                  \
+    netdev_bsd_wait,                                 \
+    netdev_bsd_alloc,                                \
+    CONSTRUCT,                                       \
+    netdev_bsd_destruct,                             \
+    netdev_bsd_dealloc,                              \
+    NULL, /* get_config */                           \
+    NULL, /* set_config */                           \
+    NULL, /* get_tunnel_config */                    \
+                                                     \
+    netdev_bsd_send,                                 \
+    netdev_bsd_send_wait,                            \
+                                                     \
+    netdev_bsd_set_etheraddr,                        \
+    netdev_bsd_get_etheraddr,                        \
+    netdev_bsd_get_mtu,                              \
+    NULL, /* set_mtu */                              \
+    netdev_bsd_get_ifindex,                          \
+    netdev_bsd_get_carrier,                          \
+    NULL, /* get_carrier_resets */                   \
+    NULL, /* set_miimon_interval */                  \
+    netdev_bsd_get_stats,                            \
+    NULL, /* set_stats */                            \
+                                                     \
+    GET_FEATURES,                                    \
+    NULL, /* set_advertisement */                    \
+    NULL, /* set_policing */                         \
+    NULL, /* get_qos_type */                         \
+    NULL, /* get_qos_capabilities */                 \
+    NULL, /* get_qos */                              \
+    NULL, /* set_qos */                              \
+    NULL, /* get_queue */                            \
+    NULL, /* set_queue */                            \
+    NULL, /* delete_queue */                         \
+    NULL, /* get_queue_stats */                      \
+    NULL, /* queue_dump_start */                     \
+    NULL, /* queue_dump_next */                      \
+    NULL, /* queue_dump_done */                      \
+    NULL, /* dump_queue_stats */                     \
+                                                     \
+    netdev_bsd_get_in4,                              \
+    netdev_bsd_set_in4,                              \
+    netdev_bsd_get_in6,                              \
+    NULL, /* add_router */                           \
+    netdev_bsd_get_next_hop,                         \
+    NULL, /* get_status */                           \
+    netdev_bsd_arp_lookup, /* arp_lookup */          \
+                                                     \
+    netdev_bsd_update_flags,                         \
+                                                     \
+    netdev_bsd_rx_alloc,                             \
+    netdev_bsd_rx_construct,                         \
+    netdev_bsd_rx_destruct,                          \
+    netdev_bsd_rx_dealloc,                           \
+    netdev_bsd_rx_recv,                              \
+    netdev_bsd_rx_wait,                              \
+    netdev_bsd_rx_drain,                             \
+}
 
-const struct netdev_class netdev_bsd_class = {
-    "system",
-
-    NULL, /* init */
-    netdev_bsd_run,
-    netdev_bsd_wait,
-    netdev_bsd_alloc,
-    netdev_bsd_construct_system,
-    netdev_bsd_destruct,
-    netdev_bsd_dealloc,
-    NULL, /* get_config */
-    NULL, /* set_config */
-    NULL, /* get_tunnel_config */
-
-    netdev_bsd_send,
-    netdev_bsd_send_wait,
-
-    netdev_bsd_set_etheraddr,
-    netdev_bsd_get_etheraddr,
-    netdev_bsd_get_mtu,
-    NULL, /* set_mtu */
-    netdev_bsd_get_ifindex,
-    netdev_bsd_get_carrier,
-    NULL, /* get_carrier_resets */
-    NULL, /* set_miimon_interval */
-    netdev_bsd_get_stats,
-    NULL, /* set_stats */
-
-    netdev_bsd_get_features,
-    NULL, /* set_advertisement */
-    NULL, /* set_policing */
-    NULL, /* get_qos_type */
-    NULL, /* get_qos_capabilities */
-    NULL, /* get_qos */
-    NULL, /* set_qos */
-    NULL, /* get_queue */
-    NULL, /* set_queue */
-    NULL, /* delete_queue */
-    NULL, /* get_queue_stats */
-    NULL, /* queue_dump_start */
-    NULL, /* queue_dump_next */
-    NULL, /* queue_dump_done */
-    NULL, /* dump_queue_stats */
-
-    netdev_bsd_get_in4,
-    netdev_bsd_set_in4,
-    netdev_bsd_get_in6,
-    NULL, /* add_router */
-    netdev_bsd_get_next_hop,
-    NULL, /* get_status */
-    netdev_bsd_arp_lookup, /* arp_lookup */
-
-    netdev_bsd_update_flags,
-
-    netdev_bsd_rx_alloc,
-    netdev_bsd_rx_construct,
-    netdev_bsd_rx_destruct,
-    netdev_bsd_rx_dealloc,
-    netdev_bsd_rx_recv,
-    netdev_bsd_rx_wait,
-    netdev_bsd_rx_drain,
-};
-
-const struct netdev_class netdev_tap_class = {
-    "tap",
-
-    NULL, /* init */
-    netdev_bsd_run,
-    netdev_bsd_wait,
-    netdev_bsd_alloc,
-    netdev_bsd_construct_tap,
-    netdev_bsd_destruct,
-    netdev_bsd_dealloc,
-    NULL, /* get_config */
-    NULL, /* set_config */
-    NULL, /* get_tunnel_config */
-
-    netdev_bsd_send,
-    netdev_bsd_send_wait,
-
-    netdev_bsd_set_etheraddr,
-    netdev_bsd_get_etheraddr,
-    netdev_bsd_get_mtu,
-    NULL, /* set_mtu */
-    netdev_bsd_get_ifindex,
-    netdev_bsd_get_carrier,
-    NULL, /* get_carrier_resets */
-    NULL, /* set_miimon_interval */
-    netdev_bsd_get_stats,
-    NULL, /* set_stats */
-
-    netdev_bsd_get_features,
-    NULL, /* set_advertisement */
-    NULL, /* set_policing */
-    NULL, /* get_qos_type */
-    NULL, /* get_qos_capabilities */
-    NULL, /* get_qos */
-    NULL, /* set_qos */
-    NULL, /* get_queue */
-    NULL, /* set_queue */
-    NULL, /* delete_queue */
-    NULL, /* get_queue_stats */
-    NULL, /* queue_dump_start */
-    NULL, /* queue_dump_next */
-    NULL, /* queue_dump_done */
-    NULL, /* dump_queue_stats */
-
-    netdev_bsd_get_in4,
-    netdev_bsd_set_in4,
-    netdev_bsd_get_in6,
-    NULL, /* add_router */
-    netdev_bsd_get_next_hop,
-    NULL, /* get_status */
-    netdev_bsd_arp_lookup, /* arp_lookup */
-
-    netdev_bsd_update_flags,
-
-    netdev_bsd_rx_alloc,
-    netdev_bsd_rx_construct,
-    netdev_bsd_rx_destruct,
-    netdev_bsd_rx_dealloc,
-    netdev_bsd_rx_recv,
-    netdev_bsd_rx_wait,
-    netdev_bsd_rx_drain,
-};
+const struct netdev_class netdev_bsd_class =
+    NETDEV_BSD_CLASS(
+        "system",
+        netdev_bsd_construct_system,
+        netdev_bsd_get_features);
+
+const struct netdev_class netdev_tap_class =
+    NETDEV_BSD_CLASS(
+        "tap",
+        netdev_bsd_construct_tap,
+        netdev_bsd_get_features);
 \f
 
 static void
index b2a7572..0f93363 100644 (file)
 #include "unaligned.h"
 #include "timeval.h"
 #include "unixctl.h"
+#include "reconnect.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_dummy);
 
-struct dummy_stream {
+struct reconnect;
+
+struct dummy_packet_stream {
     struct stream *stream;
     struct ofpbuf rxbuf;
     struct list txq;
 };
 
+enum dummy_packet_conn_type {
+    NONE,       /* No connection is configured. */
+    PASSIVE,    /* Listener. */
+    ACTIVE      /* Connect to listener. */
+};
+
+struct dummy_packet_pconn {
+    struct pstream *pstream;
+    struct dummy_packet_stream *streams;
+    size_t n_streams;
+};
+
+struct dummy_packet_rconn {
+    struct dummy_packet_stream *rstream;
+    struct reconnect *reconnect;
+};
+
+struct dummy_packet_conn {
+    enum dummy_packet_conn_type type;
+    union {
+        struct dummy_packet_pconn pconn;
+        struct dummy_packet_rconn rconn;
+    } u;
+};
+
 /* Protects 'dummy_list'. */
 static struct ovs_mutex dummy_list_mutex = OVS_MUTEX_INITIALIZER;
 
@@ -70,9 +98,7 @@ struct netdev_dummy {
     enum netdev_flags flags OVS_GUARDED;
     int ifindex OVS_GUARDED;
 
-    struct pstream *pstream OVS_GUARDED;
-    struct dummy_stream *streams OVS_GUARDED;
-    size_t n_streams OVS_GUARDED;
+    struct dummy_packet_conn conn OVS_GUARDED;
 
     FILE *tx_pcap, *rx_pcap OVS_GUARDED;
 
@@ -94,7 +120,7 @@ static unixctl_cb_func netdev_dummy_set_admin_state;
 static int netdev_dummy_construct(struct netdev *);
 static void netdev_dummy_queue_packet(struct netdev_dummy *, struct ofpbuf *);
 
-static void dummy_stream_close(struct dummy_stream *);
+static void dummy_packet_stream_close(struct dummy_packet_stream *);
 
 static bool
 is_dummy_class(const struct netdev_class *class)
@@ -117,114 +143,112 @@ netdev_rx_dummy_cast(const struct netdev_rx *rx)
 }
 
 static void
-netdev_dummy_run(void)
+dummy_packet_stream_init(struct dummy_packet_stream *s, struct stream *stream)
 {
-    struct netdev_dummy *dev;
+    int rxbuf_size = stream ? 2048 : 0;
+    s->stream = stream;
+    ofpbuf_init(&s->rxbuf, rxbuf_size);
+    list_init(&s->txq);
+}
 
-    ovs_mutex_lock(&dummy_list_mutex);
-    LIST_FOR_EACH (dev, list_node, &dummy_list) {
-        size_t i;
+static struct dummy_packet_stream *
+dummy_packet_stream_create(struct stream *stream)
+{
+    struct dummy_packet_stream *s;
 
-        ovs_mutex_lock(&dev->mutex);
+    s = xzalloc(sizeof *s);
+    dummy_packet_stream_init(s, stream);
 
-        if (dev->pstream) {
-            struct stream *new_stream;
-            int error;
-
-            error = pstream_accept(dev->pstream, &new_stream);
-            if (!error) {
-                struct dummy_stream *s;
-
-                dev->streams = xrealloc(dev->streams,
-                                        ((dev->n_streams + 1)
-                                         * sizeof *dev->streams));
-                s = &dev->streams[dev->n_streams++];
-                s->stream = new_stream;
-                ofpbuf_init(&s->rxbuf, 2048);
-                list_init(&s->txq);
-            } else if (error != EAGAIN) {
-                VLOG_WARN("%s: accept failed (%s)",
-                          pstream_get_name(dev->pstream), ovs_strerror(error));
-                pstream_close(dev->pstream);
-                dev->pstream = NULL;
-            }
-        }
+    return s;
+}
 
-        for (i = 0; i < dev->n_streams; i++) {
-            struct dummy_stream *s = &dev->streams[i];
-            int error = 0;
-            size_t n;
-
-            stream_run(s->stream);
-
-            if (!list_is_empty(&s->txq)) {
-                struct ofpbuf *txbuf;
-                int retval;
-
-                txbuf = ofpbuf_from_list(list_front(&s->txq));
-                retval = stream_send(s->stream, txbuf->data, txbuf->size);
-                if (retval > 0) {
-                    ofpbuf_pull(txbuf, retval);
-                    if (!txbuf->size) {
-                        list_remove(&txbuf->list_node);
-                        ofpbuf_delete(txbuf);
-                    }
-                } else if (retval != -EAGAIN) {
-                    error = -retval;
-                }
-            }
+static void
+dummy_packet_stream_wait(struct dummy_packet_stream *s)
+{
+    stream_run_wait(s->stream);
+    if (!list_is_empty(&s->txq)) {
+        stream_send_wait(s->stream);
+    }
+    stream_recv_wait(s->stream);
+}
 
-            if (!error) {
-                if (s->rxbuf.size < 2) {
-                    n = 2 - s->rxbuf.size;
-                } else {
-                    uint16_t frame_len;
-
-                    frame_len = ntohs(get_unaligned_be16(s->rxbuf.data));
-                    if (frame_len < ETH_HEADER_LEN) {
-                        error = EPROTO;
-                        n = 0;
-                    } else {
-                        n = (2 + frame_len) - s->rxbuf.size;
-                    }
-                }
-            }
-            if (!error) {
-                int retval;
-
-                ofpbuf_prealloc_tailroom(&s->rxbuf, n);
-                retval = stream_recv(s->stream, ofpbuf_tail(&s->rxbuf), n);
-                if (retval > 0) {
-                    s->rxbuf.size += retval;
-                    if (retval == n && s->rxbuf.size > 2) {
-                        ofpbuf_pull(&s->rxbuf, 2);
-                        netdev_dummy_queue_packet(dev,
-                                                  ofpbuf_clone(&s->rxbuf));
-                        ofpbuf_clear(&s->rxbuf);
-                    }
-                } else if (retval != -EAGAIN) {
-                    error = (retval < 0 ? -retval
-                             : s->rxbuf.size ? EPROTO
-                             : EOF);
-                }
-            }
+static void
+dummy_packet_stream_send(struct dummy_packet_stream *s, const void *buffer, size_t size)
+{
+    if (list_size(&s->txq) < NETDEV_DUMMY_MAX_QUEUE) {
+        struct ofpbuf *b;
 
-            if (error) {
-                VLOG_DBG("%s: closing connection (%s)",
-                         stream_get_name(s->stream),
-                         ovs_retval_to_string(error));
-                dummy_stream_close(&dev->streams[i]);
-                dev->streams[i] = dev->streams[--dev->n_streams];
+        b = ofpbuf_clone_data_with_headroom(buffer, size, 2);
+        put_unaligned_be16(ofpbuf_push_uninit(b, 2), htons(size));
+        list_push_back(&s->txq, &b->list_node);
+    }
+}
+
+static int
+dummy_packet_stream_run(struct netdev_dummy *dev, struct dummy_packet_stream *s)
+{
+    int error = 0;
+    size_t n;
+
+    stream_run(s->stream);
+
+    if (!list_is_empty(&s->txq)) {
+        struct ofpbuf *txbuf;
+        int retval;
+
+        txbuf = ofpbuf_from_list(list_front(&s->txq));
+        retval = stream_send(s->stream, txbuf->data, txbuf->size);
+        if (retval > 0) {
+            ofpbuf_pull(txbuf, retval);
+            if (!txbuf->size) {
+                list_remove(&txbuf->list_node);
+                ofpbuf_delete(txbuf);
             }
+        } else if (retval != -EAGAIN) {
+            error = -retval;
         }
+    }
 
-        ovs_mutex_unlock(&dev->mutex);
+    if (!error) {
+        if (s->rxbuf.size < 2) {
+            n = 2 - s->rxbuf.size;
+        } else {
+            uint16_t frame_len;
+
+            frame_len = ntohs(get_unaligned_be16(s->rxbuf.data));
+            if (frame_len < ETH_HEADER_LEN) {
+                error = EPROTO;
+                n = 0;
+            } else {
+                n = (2 + frame_len) - s->rxbuf.size;
+            }
+        }
     }
-    ovs_mutex_unlock(&dummy_list_mutex);
+    if (!error) {
+        int retval;
+
+        ofpbuf_prealloc_tailroom(&s->rxbuf, n);
+        retval = stream_recv(s->stream, ofpbuf_tail(&s->rxbuf), n);
+        if (retval > 0) {
+            s->rxbuf.size += retval;
+            if (retval == n && s->rxbuf.size > 2) {
+                ofpbuf_pull(&s->rxbuf, 2);
+                netdev_dummy_queue_packet(dev,
+                                          ofpbuf_clone(&s->rxbuf));
+                ofpbuf_clear(&s->rxbuf);
+            }
+        } else if (retval != -EAGAIN) {
+            error = (retval < 0 ? -retval
+                     : s->rxbuf.size ? EPROTO
+                     : EOF);
+        }
+    }
+
+    return error;
 }
 
 static void
-dummy_stream_close(struct dummy_stream *s)
+dummy_packet_stream_close(struct dummy_packet_stream *s)
 {
     stream_close(s->stream);
     ofpbuf_uninit(&s->rxbuf);
@@ -232,27 +256,319 @@ dummy_stream_close(struct dummy_stream *s)
 }
 
 static void
-netdev_dummy_wait(void)
+dummy_packet_conn_init(struct dummy_packet_conn *conn)
 {
-    struct netdev_dummy *dev;
+    memset(conn, 0, sizeof *conn);
+    conn->type = NONE;
+}
 
-    ovs_mutex_lock(&dummy_list_mutex);
-    LIST_FOR_EACH (dev, list_node, &dummy_list) {
-        size_t i;
+static void
+dummy_packet_conn_get_config(struct dummy_packet_conn *conn, struct smap *args)
+{
 
-        ovs_mutex_lock(&dev->mutex);
-        if (dev->pstream) {
-            pstream_wait(dev->pstream);
+    switch (conn->type) {
+    case PASSIVE:
+        smap_add(args, "pstream", pstream_get_name(conn->u.pconn.pstream));
+        break;
+
+    case ACTIVE:
+        smap_add(args, "stream", stream_get_name(conn->u.rconn.rstream->stream));
+        break;
+
+    case NONE:
+    default:
+        break;
+    }
+}
+
+static void
+dummy_packet_conn_close(struct dummy_packet_conn *conn)
+{
+    int i;
+    struct dummy_packet_pconn *pconn = &conn->u.pconn;
+    struct dummy_packet_rconn *rconn = &conn->u.rconn;
+
+    switch (conn->type) {
+    case PASSIVE:
+        pstream_close(pconn->pstream);
+        for (i = 0; i < pconn->n_streams; i++) {
+            dummy_packet_stream_close(&pconn->streams[i]);
+        }
+        free(pconn->streams);
+        pconn->pstream = NULL;
+        pconn->streams = NULL;
+        break;
+
+    case ACTIVE:
+        dummy_packet_stream_close(rconn->rstream);
+        free(rconn->rstream);
+        rconn->rstream = NULL;
+        reconnect_destroy(rconn->reconnect);
+        rconn->reconnect = NULL;
+        break;
+
+    case NONE:
+    default:
+        break;
+    }
+
+    conn->type = NONE;
+    memset(conn, 0, sizeof *conn);
+}
+
+static void
+dummy_packet_conn_set_config(struct dummy_packet_conn *conn,
+                             const struct smap *args)
+{
+    const char *pstream = smap_get(args, "pstream");
+    const char *stream = smap_get(args, "stream");
+
+    if (pstream && stream) {
+         VLOG_WARN("Open failed: both %s and %s are configured",
+                   pstream, stream);
+         return;
+    }
+
+    switch (conn->type) {
+    case PASSIVE:
+        if (!strcmp(pstream_get_name(conn->u.pconn.pstream), pstream)) {
+            return;
+        }
+        dummy_packet_conn_close(conn);
+        break;
+    case ACTIVE:
+        if (!strcmp(stream_get_name(conn->u.rconn.rstream->stream), stream)) {
+            return;
+        }
+        dummy_packet_conn_close(conn);
+        break;
+    case NONE:
+    default:
+        break;
+    }
+
+    if (pstream) {
+        int error;
+
+        error = pstream_open(pstream, &conn->u.pconn.pstream, DSCP_DEFAULT);
+        if (error) {
+            VLOG_WARN("%s: open failed (%s)", pstream, ovs_strerror(error));
+        } else {
+            conn->type = PASSIVE;
+        }
+    }
+
+    if (stream) {
+        int error;
+        struct stream *active_stream;
+        struct reconnect *reconnect;;
+
+        reconnect = reconnect_create(time_msec());
+        reconnect_set_name(reconnect, stream);
+        reconnect_set_passive(reconnect, false, time_msec());
+        reconnect_enable(reconnect, time_msec());
+        reconnect_set_backoff(reconnect, 1000, INT_MAX);
+        conn->u.rconn.reconnect = reconnect;
+
+        error = stream_open(stream, &active_stream, DSCP_DEFAULT);
+        conn->u.rconn.rstream = dummy_packet_stream_create(active_stream);
+
+        switch (error) {
+        case 0:
+            reconnect_connected(conn->u.rconn.reconnect, time_msec());
+            conn->type = ACTIVE;
+            break;
+
+        case EAGAIN:
+            reconnect_connecting(conn->u.rconn.reconnect, time_msec());
+            break;
+
+        default:
+            reconnect_connecting(conn->u.rconn.reconnect, time_msec());
+            stream_close(active_stream);
+            break;
+        }
+    }
+}
+
+static void
+dummy_pconn_run(struct netdev_dummy *dev)
+    OVS_REQUIRES(dev->mutex)
+{
+    struct stream *new_stream;
+    struct dummy_packet_pconn *pconn = &dev->conn.u.pconn;
+    int error;
+    size_t i;
+
+    error = pstream_accept(pconn->pstream, &new_stream);
+    if (!error) {
+        struct dummy_packet_stream *s;
+
+        pconn->streams = xrealloc(pconn->streams,
+                                ((pconn->n_streams + 1)
+                                 * sizeof *s));
+        s = &pconn->streams[pconn->n_streams++];
+        dummy_packet_stream_init(s, new_stream);
+    } else if (error != EAGAIN) {
+        VLOG_WARN("%s: accept failed (%s)",
+                  pstream_get_name(pconn->pstream), ovs_strerror(error));
+        pstream_close(pconn->pstream);
+        pconn->pstream = NULL;
+        dev->conn.type = NONE;
+    }
+
+    for (i = 0; i < pconn->n_streams; i++) {
+        struct dummy_packet_stream *s = &pconn->streams[i];
+
+        error = dummy_packet_stream_run(dev, s);
+        if (error) {
+            VLOG_DBG("%s: closing connection (%s)",
+                     stream_get_name(s->stream),
+                     ovs_retval_to_string(error));
+            dummy_packet_stream_close(s);
+            pconn->streams[i] = pconn->streams[--pconn->n_streams];
         }
-        for (i = 0; i < dev->n_streams; i++) {
-            struct dummy_stream *s = &dev->streams[i];
+    }
+}
 
-            stream_run_wait(s->stream);
-            if (!list_is_empty(&s->txq)) {
-                stream_send_wait(s->stream);
+static void
+dummy_rconn_run(struct netdev_dummy *dev)
+OVS_REQUIRES(dev->mutex)
+{
+    struct dummy_packet_rconn *rconn = &dev->conn.u.rconn;
+
+    switch (reconnect_run(rconn->reconnect, time_msec())) {
+    case RECONNECT_CONNECT:
+        {
+            int err = stream_connect(rconn->rstream->stream);
+
+            switch (err) {
+            case 0: /* Connected. */
+                reconnect_connected(rconn->reconnect, time_msec());
+                dev->conn.type = ACTIVE;
+                break;
+
+            case EAGAIN:
+                reconnect_connecting(rconn->reconnect, time_msec());
+                return;
+
+            default:
+                reconnect_connect_failed(rconn->reconnect, time_msec(), err);
+                stream_close(rconn->rstream->stream);
+                return;
             }
-            stream_recv_wait(s->stream);
         }
+        break;
+
+    case RECONNECT_DISCONNECT:
+    case RECONNECT_PROBE:
+    default:
+        break;
+    }
+
+    if (reconnect_is_connected(rconn->reconnect)) {
+        int err;
+
+        err = dummy_packet_stream_run(dev, rconn->rstream);
+
+        if (err) {
+            reconnect_disconnected(rconn->reconnect, time_msec(), err);
+            stream_close(rconn->rstream->stream);
+        }
+    }
+}
+
+static void
+dummy_packet_conn_run(struct netdev_dummy *dev)
+    OVS_REQUIRES(dev->mutex)
+{
+    switch (dev->conn.type) {
+    case PASSIVE:
+        dummy_pconn_run(dev);
+        break;
+
+    case ACTIVE:
+        dummy_rconn_run(dev);
+        break;
+
+    case NONE:
+    default:
+        break;
+    }
+}
+
+static void
+dummy_packet_conn_wait(struct dummy_packet_conn *conn)
+{
+    int i;
+    switch (conn->type) {
+    case PASSIVE:
+        pstream_wait(conn->u.pconn.pstream);
+        for (i = 0; i < conn->u.pconn.n_streams; i++) {
+            struct dummy_packet_stream *s = &conn->u.pconn.streams[i];
+            dummy_packet_stream_wait(s);
+        }
+        break;
+    case ACTIVE:
+        dummy_packet_stream_wait(conn->u.rconn.rstream);
+        break;
+
+    case NONE:
+    default:
+        break;
+    }
+}
+
+static void
+dummy_packet_conn_send(struct dummy_packet_conn *conn,
+                       const void *buffer, size_t size)
+{
+    int i;
+
+    switch (conn->type) {
+    case PASSIVE:
+        for (i = 0; i < conn->u.pconn.n_streams; i++) {
+            struct dummy_packet_stream *s = &conn->u.pconn.streams[i];
+
+            dummy_packet_stream_send(s, buffer, size);
+            pstream_wait(conn->u.pconn.pstream);
+        }
+        break;
+
+    case ACTIVE:
+        dummy_packet_stream_send(conn->u.rconn.rstream, buffer, size);
+        dummy_packet_stream_wait(conn->u.rconn.rstream);
+        break;
+
+    case NONE:
+    default:
+        break;
+    }
+}
+
+static void
+netdev_dummy_run(void)
+{
+    struct netdev_dummy *dev;
+
+    ovs_mutex_lock(&dummy_list_mutex);
+    LIST_FOR_EACH (dev, list_node, &dummy_list) {
+        ovs_mutex_lock(&dev->mutex);
+        dummy_packet_conn_run(dev);
+        ovs_mutex_unlock(&dev->mutex);
+    }
+    ovs_mutex_unlock(&dummy_list_mutex);
+}
+
+static void
+netdev_dummy_wait(void)
+{
+    struct netdev_dummy *dev;
+
+    ovs_mutex_lock(&dummy_list_mutex);
+    LIST_FOR_EACH (dev, list_node, &dummy_list) {
+        ovs_mutex_lock(&dev->mutex);
+        dummy_packet_conn_wait(&dev->conn);
         ovs_mutex_unlock(&dev->mutex);
     }
     ovs_mutex_unlock(&dummy_list_mutex);
@@ -286,9 +602,7 @@ netdev_dummy_construct(struct netdev *netdev_)
     netdev->flags = 0;
     netdev->ifindex = -EOPNOTSUPP;
 
-    netdev->pstream = NULL;
-    netdev->streams = NULL;
-    netdev->n_streams = 0;
+    dummy_packet_conn_init(&netdev->conn);
 
     list_init(&netdev->rxes);
     ovs_mutex_unlock(&netdev->mutex);
@@ -304,18 +618,15 @@ static void
 netdev_dummy_destruct(struct netdev *netdev_)
 {
     struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
-    size_t i;
 
     ovs_mutex_lock(&dummy_list_mutex);
     list_remove(&netdev->list_node);
     ovs_mutex_unlock(&dummy_list_mutex);
 
     ovs_mutex_lock(&netdev->mutex);
-    pstream_close(netdev->pstream);
-    for (i = 0; i < netdev->n_streams; i++) {
-        dummy_stream_close(&netdev->streams[i]);
-    }
-    free(netdev->streams);
+    dummy_packet_conn_close(&netdev->conn);
+    netdev->conn.type = NONE;
+
     ovs_mutex_unlock(&netdev->mutex);
     ovs_mutex_destroy(&netdev->mutex);
 }
@@ -339,9 +650,7 @@ netdev_dummy_get_config(const struct netdev *netdev_, struct smap *args)
         smap_add_format(args, "ifindex", "%d", netdev->ifindex);
     }
 
-    if (netdev->pstream) {
-        smap_add(args, "pstream", pstream_get_name(netdev->pstream));
-    }
+    dummy_packet_conn_get_config(&netdev->conn, args);
 
     ovs_mutex_unlock(&netdev->mutex);
     return 0;
@@ -351,29 +660,12 @@ static int
 netdev_dummy_set_config(struct netdev *netdev_, const struct smap *args)
 {
     struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
-    const char *pstream;
     const char *pcap;
 
     ovs_mutex_lock(&netdev->mutex);
     netdev->ifindex = smap_get_int(args, "ifindex", -EOPNOTSUPP);
 
-    pstream = smap_get(args, "pstream");
-    if (!pstream
-        || !netdev->pstream
-        || strcmp(pstream_get_name(netdev->pstream), pstream)) {
-        pstream_close(netdev->pstream);
-        netdev->pstream = NULL;
-
-        if (pstream) {
-            int error;
-
-            error = pstream_open(pstream, &netdev->pstream, DSCP_DEFAULT);
-            if (error) {
-                VLOG_WARN("%s: open failed (%s)",
-                          pstream, ovs_strerror(error));
-            }
-        }
-    }
+    dummy_packet_conn_set_config(&netdev->conn, args);
 
     if (netdev->rx_pcap) {
         fclose(netdev->rx_pcap);
@@ -384,16 +676,16 @@ netdev_dummy_set_config(struct netdev *netdev_, const struct smap *args)
     netdev->rx_pcap = netdev->tx_pcap = NULL;
     pcap = smap_get(args, "pcap");
     if (pcap) {
-        netdev->rx_pcap = netdev->tx_pcap = pcap_open(pcap, "ab");
+        netdev->rx_pcap = netdev->tx_pcap = ovs_pcap_open(pcap, "ab");
     } else {
         const char *rx_pcap = smap_get(args, "rx_pcap");
         const char *tx_pcap = smap_get(args, "tx_pcap");
 
         if (rx_pcap) {
-            netdev->rx_pcap = pcap_open(rx_pcap, "ab");
+            netdev->rx_pcap = ovs_pcap_open(rx_pcap, "ab");
         }
         if (tx_pcap) {
-            netdev->tx_pcap = pcap_open(tx_pcap, "ab");
+            netdev->tx_pcap = ovs_pcap_open(tx_pcap, "ab");
         }
     }
 
@@ -520,7 +812,6 @@ static int
 netdev_dummy_send(struct netdev *netdev, const void *buffer, size_t size)
 {
     struct netdev_dummy *dev = netdev_dummy_cast(netdev);
-    size_t i;
 
     if (size < ETH_HEADER_LEN) {
         return EMSGSIZE;
@@ -544,25 +835,16 @@ netdev_dummy_send(struct netdev *netdev, const void *buffer, size_t size)
     dev->stats.tx_packets++;
     dev->stats.tx_bytes += size;
 
+    dummy_packet_conn_send(&dev->conn, buffer, size);
+
     if (dev->tx_pcap) {
         struct ofpbuf packet;
 
         ofpbuf_use_const(&packet, buffer, size);
-        pcap_write(dev->tx_pcap, &packet);
+        ovs_pcap_write(dev->tx_pcap, &packet);
         fflush(dev->tx_pcap);
     }
 
-    for (i = 0; i < dev->n_streams; i++) {
-        struct dummy_stream *s = &dev->streams[i];
-
-        if (list_size(&s->txq) < NETDEV_DUMMY_MAX_QUEUE) {
-            struct ofpbuf *b;
-
-            b = ofpbuf_clone_data_with_headroom(buffer, size, 2);
-            put_unaligned_be16(ofpbuf_push_uninit(b, 2), htons(size));
-            list_push_back(&s->txq, &b->list_node);
-        }
-    }
     ovs_mutex_unlock(&dev->mutex);
 
     return 0;
@@ -814,7 +1096,7 @@ netdev_dummy_queue_packet(struct netdev_dummy *dummy, struct ofpbuf *packet)
     struct netdev_rx_dummy *rx, *prev;
 
     if (dummy->rx_pcap) {
-        pcap_write(dummy->rx_pcap, packet);
+        ovs_pcap_write(dummy->rx_pcap, packet);
         fflush(dummy->rx_pcap);
     }
     prev = NULL;
index 1463ff0..8cb1b8e 100644 (file)
@@ -699,7 +699,7 @@ nl_dump_start(struct nl_dump *dump, int protocol, const struct ofpbuf *request)
     nl_msg_nlmsghdr(request)->nlmsg_flags |= NLM_F_DUMP | NLM_F_ACK;
     dump->status = nl_sock_send__(dump->sock, request,
                                   nl_sock_allocate_seq(dump->sock, 1), true);
-    dump->seq = nl_msg_nlmsghdr(request)->nlmsg_seq;
+    dump->nl_seq = nl_msg_nlmsghdr(request)->nlmsg_seq;
 }
 
 /* Helper function for nl_dump_next(). */
@@ -715,9 +715,9 @@ nl_dump_recv(struct nl_dump *dump)
     }
 
     nlmsghdr = nl_msg_nlmsghdr(&dump->buffer);
-    if (dump->seq != nlmsghdr->nlmsg_seq) {
+    if (dump->nl_seq != nlmsghdr->nlmsg_seq) {
         VLOG_DBG_RL(&rl, "ignoring seq %#"PRIx32" != expected %#"PRIx32,
-                    nlmsghdr->nlmsg_seq, dump->seq);
+                    nlmsghdr->nlmsg_seq, dump->nl_seq);
         return EAGAIN;
     }
 
index 18db417..5fedfe9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -98,7 +98,7 @@ void nl_transact_multiple(int protocol, struct nl_transaction **, size_t n);
 /* Table dumping. */
 struct nl_dump {
     struct nl_sock *sock;       /* Socket being dumped. */
-    uint32_t seq;               /* Expected nlmsg_seq for replies. */
+    uint32_t nl_seq;            /* Expected nlmsg_seq for replies. */
     struct ofpbuf buffer;       /* Receive buffer currently being iterated. */
     int status;                 /* 0=OK, EOF=done, or positive errno value. */
 };
index 983fd7d..437e85b 100644 (file)
@@ -572,7 +572,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
     int match_len;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     /* Metadata. */
     if (match->wc.masks.in_port.ofp_port) {
@@ -616,17 +616,17 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
 
     /* MPLS. */
     if (eth_type_mpls(flow->dl_type)) {
-        if (match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK)) {
-            nxm_put_8(b, OXM_OF_MPLS_TC, mpls_lse_to_tc(flow->mpls_lse));
+        if (match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) {
+            nxm_put_8(b, OXM_OF_MPLS_TC, mpls_lse_to_tc(flow->mpls_lse[0]));
         }
 
-        if (match->wc.masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
-            nxm_put_8(b, OXM_OF_MPLS_BOS, mpls_lse_to_bos(flow->mpls_lse));
+        if (match->wc.masks.mpls_lse[0] & htonl(MPLS_BOS_MASK)) {
+            nxm_put_8(b, OXM_OF_MPLS_BOS, mpls_lse_to_bos(flow->mpls_lse[0]));
         }
 
-        if (match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+        if (match->wc.masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK)) {
             nxm_put_32(b, OXM_OF_MPLS_LABEL,
-                       htonl(mpls_lse_to_label(flow->mpls_lse)));
+                       htonl(mpls_lse_to_label(flow->mpls_lse[0])));
         }
     }
 
index 873e05a..b81d0d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -353,20 +353,34 @@ format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse)
 
 static void
 format_mpls(struct ds *ds, const struct ovs_key_mpls *mpls_key,
-            const struct ovs_key_mpls *mpls_mask)
+            const struct ovs_key_mpls *mpls_mask, int n)
 {
-    ovs_be32 key = mpls_key->mpls_lse;
+    if (n == 1) {
+        ovs_be32 key = mpls_key->mpls_lse;
 
-    if (mpls_mask == NULL) {
-        format_mpls_lse(ds, key);
+        if (mpls_mask == NULL) {
+            format_mpls_lse(ds, key);
+        } else {
+            ovs_be32 mask = mpls_mask->mpls_lse;
+
+            ds_put_format(ds, "label=%"PRIu32"/0x%x,tc=%d/%x,ttl=%d/0x%x,bos=%d/%x",
+                          mpls_lse_to_label(key), mpls_lse_to_label(mask),
+                          mpls_lse_to_tc(key), mpls_lse_to_tc(mask),
+                          mpls_lse_to_ttl(key), mpls_lse_to_ttl(mask),
+                          mpls_lse_to_bos(key), mpls_lse_to_bos(mask));
+        }
     } else {
-        ovs_be32 mask = mpls_mask->mpls_lse;
+        int i;
 
-        ds_put_format(ds, "label=%"PRIu32"/0x%x,tc=%d/%x,ttl=%d/0x%x,bos=%d/%x",
-                  mpls_lse_to_label(key), mpls_lse_to_label(mask),
-                  mpls_lse_to_tc(key), mpls_lse_to_tc(mask),
-                  mpls_lse_to_ttl(key), mpls_lse_to_ttl(mask),
-                  mpls_lse_to_bos(key), mpls_lse_to_bos(mask));
+        for (i = 0; i < n; i++) {
+            ds_put_format(ds, "lse%d=%#"PRIx32,
+                          i, ntohl(mpls_key[i].mpls_lse));
+            if (mpls_mask) {
+                ds_put_format(ds, "/%#"PRIx32, ntohl(mpls_mask[i].mpls_lse));
+            }
+            ds_put_char(ds, ',');
+        }
+        ds_chomp(ds, ',');
     }
 }
 
@@ -722,7 +736,7 @@ odp_flow_key_attr_len(uint16_t type)
     case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
     case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16);
     case OVS_KEY_ATTR_ETHERTYPE: return 2;
-    case OVS_KEY_ATTR_MPLS: return sizeof(struct ovs_key_mpls);
+    case OVS_KEY_ATTR_MPLS: return -2;
     case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4);
     case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
     case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp);
@@ -1116,10 +1130,23 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
     case OVS_KEY_ATTR_MPLS: {
         const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
         const struct ovs_key_mpls *mpls_mask = NULL;
+        size_t size = nl_attr_get_size(a);
+
+        if (!size || size % sizeof *mpls_key) {
+            ds_put_format(ds, "(bad key length %"PRIuSIZE")",
+                          nl_attr_get_size(a));
+            return;
+        }
         if (!is_exact) {
             mpls_mask = nl_attr_get(ma);
+            if (nl_attr_get_size(a) != nl_attr_get_size(ma)) {
+                ds_put_format(ds, "(key length %"PRIuSIZE" != "
+                              "mask length %"PRIuSIZE")",
+                              nl_attr_get_size(a), nl_attr_get_size(ma));
+                return;
+            }
         }
-        format_mpls(ds, mpls_key, mpls_mask);
+        format_mpls(ds, mpls_key, mpls_mask, size / sizeof *mpls_key);
         break;
     }
 
@@ -2392,7 +2419,8 @@ ovs_to_odp_frag_mask(uint8_t nw_frag_mask)
 
 static void
 odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
-                         const struct flow *flow, odp_port_t odp_in_port)
+                         const struct flow *flow, odp_port_t odp_in_port,
+                         size_t max_mpls_depth)
 {
     bool is_mask;
     struct ovs_key_ethernet *eth_key;
@@ -2494,10 +2522,15 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
         memcpy(arp_key->arp_tha, data->arp_tha, ETH_ADDR_LEN);
     } else if (eth_type_mpls(flow->dl_type)) {
         struct ovs_key_mpls *mpls_key;
+        int i, n;
 
+        n = flow_count_mpls_labels(flow, NULL);
+        n = MIN(n, max_mpls_depth);
         mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
-                                            sizeof *mpls_key);
-        mpls_key->mpls_lse = data->mpls_lse;
+                                            n * sizeof *mpls_key);
+        for (i = 0; i < n; i++) {
+            mpls_key[i].mpls_lse = data->mpls_lse[i];
+        }
     }
 
     if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
@@ -2579,7 +2612,7 @@ void
 odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
                        odp_port_t odp_in_port)
 {
-    odp_flow_key_from_flow__(buf, flow, flow, odp_in_port);
+    odp_flow_key_from_flow__(buf, flow, flow, odp_in_port, SIZE_MAX);
 }
 
 /* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
@@ -2592,9 +2625,11 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
  * capable of being expanded to allow for that much space. */
 void
 odp_flow_key_from_mask(struct ofpbuf *buf, const struct flow *mask,
-                       const struct flow *flow, uint32_t odp_in_port_mask)
+                       const struct flow *flow, uint32_t odp_in_port_mask,
+                       size_t max_mpls_depth)
 {
-    odp_flow_key_from_flow__(buf, mask, flow, u32_to_odp(odp_in_port_mask));
+    odp_flow_key_from_flow__(buf, mask, flow, u32_to_odp(odp_in_port_mask),
+                             max_mpls_depth);
 }
 
 /* Generate ODP flow key from the given packet metadata */
@@ -2850,21 +2885,50 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     enum ovs_key_attr expected_bit = 0xff;
 
     if (eth_type_mpls(src_flow->dl_type)) {
+        size_t size = nl_attr_get_size(attrs[OVS_KEY_ATTR_MPLS]);
+        const ovs_be32 *mpls_lse = nl_attr_get(attrs[OVS_KEY_ATTR_MPLS]);
+        int n = size / sizeof(ovs_be32);
+        int i;
+
+        if (!size || size % sizeof(ovs_be32)) {
+            return ODP_FIT_ERROR;
+        }
+
         if (!is_mask) {
             expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
 
             if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
                 return ODP_FIT_TOO_LITTLE;
             }
-            flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
         } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
-            flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
-
-            if (flow->mpls_lse != 0 && flow->dl_type != htons(0xffff)) {
+            if (flow->mpls_lse[0] && flow->dl_type != htons(0xffff)) {
                 return ODP_FIT_ERROR;
             }
             expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
         }
+
+        for (i = 0; i < n && i < FLOW_MAX_MPLS_LABELS; i++) {
+            flow->mpls_lse[i] = mpls_lse[i];
+        }
+        if (n > FLOW_MAX_MPLS_LABELS) {
+            return ODP_FIT_TOO_MUCH;
+        }
+
+        if (!is_mask) {
+            /* BOS may be set only in the innermost label. */
+            for (i = 0; i < n - 1; i++) {
+                if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+                    return ODP_FIT_ERROR;
+                }
+            }
+
+            /* BOS must be set in the innermost label. */
+            if (n < FLOW_MAX_MPLS_LABELS
+                && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
+                return ODP_FIT_TOO_LITTLE;
+            }
+        }
+
         goto done;
     } else if (src_flow->dl_type == htons(ETH_TYPE_IP)) {
         if (!is_mask) {
@@ -3398,19 +3462,26 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
 }
 
 static void
-commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
-                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+pop_vlan(struct flow *base,
+         struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    if (base->vlan_tci == vlan_tci) {
-        return;
-    }
-
     memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
 
     if (base->vlan_tci & htons(VLAN_CFI)) {
         nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+        base->vlan_tci = 0;
+    }
+}
+
+static void
+commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+{
+    if (base->vlan_tci == vlan_tci) {
+        return;
     }
 
+    pop_vlan(base, odp_actions, wc);
     if (vlan_tci & htons(VLAN_CFI)) {
         struct ovs_action_push_vlan vlan;
 
@@ -3424,43 +3495,72 @@ commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
 
 static void
 commit_mpls_action(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions, struct flow_wildcards *wc,
-                   int *mpls_depth_delta)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    if (flow->mpls_lse == base->mpls_lse && !*mpls_depth_delta) {
-        return;
+    int base_n = flow_count_mpls_labels(base, wc);
+    int flow_n = flow_count_mpls_labels(flow, wc);
+    int common_n = flow_count_common_mpls_labels(flow, flow_n, base, base_n,
+                                                 wc);
+
+    while (base_n > common_n) {
+        if (base_n - 1 == common_n && flow_n > common_n) {
+            /* If there is only one more LSE in base than there are common
+             * between base and flow; and flow has at least one more LSE than
+             * is common then the topmost LSE of base may be updated using
+             * set */
+            struct ovs_key_mpls mpls_key;
+
+            mpls_key.mpls_lse = flow->mpls_lse[flow_n - base_n];
+            commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
+                              &mpls_key, sizeof mpls_key);
+            flow_set_mpls_lse(base, 0, mpls_key.mpls_lse);
+            common_n++;
+        } else {
+            /* Otherwise, if there more LSEs in base than are common between
+             * base and flow then pop the topmost one. */
+            ovs_be16 dl_type;
+            bool popped;
+
+            /* If all the LSEs are to be popped and this is not the outermost
+             * LSE then use ETH_TYPE_MPLS as the ethertype parameter of the
+             * POP_MPLS action instead of flow->dl_type.
+             *
+             * This is because the POP_MPLS action requires its ethertype
+             * argument to be an MPLS ethernet type but in this case
+             * flow->dl_type will be a non-MPLS ethernet type.
+             *
+             * When the final POP_MPLS action occurs it use flow->dl_type and
+             * the and the resulting packet will have the desired dl_type. */
+            if ((!eth_type_mpls(flow->dl_type)) && base_n > 1) {
+                dl_type = htons(ETH_TYPE_MPLS);
+            } else {
+                dl_type = flow->dl_type;
+            }
+            nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, dl_type);
+            popped = flow_pop_mpls(base, base_n, flow->dl_type, wc);
+            ovs_assert(popped);
+            base_n--;
+        }
     }
 
-    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
-
-    switch (*mpls_depth_delta) {
-    case -1:
-        nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, flow->dl_type);
-        break;
-    case 1: {
+    /* If, after the above popping and setting, there are more LSEs in flow
+     * than base then some LSEs need to be pushed. */
+    while (base_n < flow_n) {
         struct ovs_action_push_mpls *mpls;
 
-        mpls = nl_msg_put_unspec_zero(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
+        /* If there's a VLAN tag, pop it off so that our new MPLS label doesn't
+         * end up outside it. */
+        pop_vlan(base, odp_actions, wc);
+
+        mpls = nl_msg_put_unspec_zero(odp_actions,
+                                      OVS_ACTION_ATTR_PUSH_MPLS,
                                       sizeof *mpls);
         mpls->mpls_ethertype = flow->dl_type;
-        mpls->mpls_lse = flow->mpls_lse;
-        break;
+        mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1];
+        flow_push_mpls(base, base_n, mpls->mpls_ethertype, wc);
+        flow_set_mpls_lse(base, 0, mpls->mpls_lse);
+        base_n++;
     }
-    case 0: {
-        struct ovs_key_mpls mpls_key;
-
-        mpls_key.mpls_lse = flow->mpls_lse;
-        commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
-                          &mpls_key, sizeof(mpls_key));
-        break;
-    }
-    default:
-        OVS_NOT_REACHED();
-    }
-
-    base->dl_type = flow->dl_type;
-    base->mpls_lse = flow->mpls_lse;
-    *mpls_depth_delta = 0;
 }
 
 static void
@@ -3681,20 +3781,15 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base,
  * slow path, if there is one, otherwise 0. */
 enum slow_path_reason
 commit_odp_actions(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions, struct flow_wildcards *wc,
-                   int *mpls_depth_delta)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     enum slow_path_reason slow;
 
     commit_set_ether_addr_action(flow, base, odp_actions, wc);
-    commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
     slow = commit_set_nw_action(flow, base, odp_actions, wc);
     commit_set_port_action(flow, base, odp_actions, wc);
-    /* Committing MPLS actions should occur after committing nw and port
-     * actions. This is because committing MPLS actions may alter a packet so
-     * that it is no longer IP and thus nw and port actions are no longer valid.
-     */
-    commit_mpls_action(flow, base, odp_actions, wc, mpls_depth_delta);
+    commit_mpls_action(flow, base, odp_actions, wc);
+    commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
     commit_set_priority_action(flow, base, odp_actions, wc);
     commit_set_pkt_mark_action(flow, base, odp_actions, wc);
 
index 2b62614..7bc64c7 100644 (file)
@@ -146,7 +146,8 @@ int odp_flow_from_string(const char *s,
 void odp_flow_key_from_flow(struct ofpbuf *, const struct flow *,
                             odp_port_t odp_in_port);
 void odp_flow_key_from_mask(struct ofpbuf *, const struct flow *mask,
-                            const struct flow *flow, uint32_t odp_in_port);
+                            const struct flow *flow, uint32_t odp_in_port,
+                            size_t max_mpls_depth);
 
 uint32_t odp_flow_key_hash(const struct nlattr *, size_t);
 
@@ -181,8 +182,7 @@ void commit_odp_tunnel_action(const struct flow *, struct flow *base,
 enum slow_path_reason commit_odp_actions(const struct flow *,
                                          struct flow *base,
                                          struct ofpbuf *odp_actions,
-                                         struct flow_wildcards *wc,
-                                         int *mpls_depth_delta);
+                                         struct flow_wildcards *wc);
 \f
 /* ofproto-dpif interface.
  *
index 251adfa..b254ac6 100644 (file)
@@ -639,7 +639,9 @@ parse_named_action(enum ofputil_action_code code,
     char *error = NULL;
     uint16_t ethertype = 0;
     uint16_t vid = 0;
-    uint8_t tos = 0, ecn, ttl;
+    uint8_t tos = 0;
+    uint8_t ecn = 0;
+    uint8_t ttl = 0;
     uint8_t pcp = 0;
 
     switch (code) {
index a0a372f..7de82c6 100644 (file)
@@ -84,7 +84,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -437,10 +437,10 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
 
     if (eth_type_mpls(match->flow.dl_type)) {
         if (!(wc & OFPFW11_MPLS_LABEL)) {
-            match_set_mpls_label(match, ofmatch->mpls_label);
+            match_set_mpls_label(match, 0, ofmatch->mpls_label);
         }
         if (!(wc & OFPFW11_MPLS_TC)) {
-            match_set_mpls_tc(match, ofmatch->mpls_tc);
+            match_set_mpls_tc(match, 0, ofmatch->mpls_tc);
         }
     }
 
@@ -533,16 +533,17 @@ ofputil_match_to_ofp11_match(const struct match *match,
         ofmatch->tp_dst = match->flow.tp_dst;
     }
 
-    if (!(match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK))) {
+    if (!(match->wc.masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK))) {
         wc |= OFPFW11_MPLS_LABEL;
     } else {
-        ofmatch->mpls_label = htonl(mpls_lse_to_label(match->flow.mpls_lse));
+        ofmatch->mpls_label = htonl(mpls_lse_to_label(
+                                        match->flow.mpls_lse[0]));
     }
 
-    if (!(match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK))) {
+    if (!(match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK))) {
         wc |= OFPFW11_MPLS_TC;
     } else {
-        ofmatch->mpls_tc = mpls_lse_to_tc(match->flow.mpls_lse);
+        ofmatch->mpls_tc = mpls_lse_to_tc(match->flow.mpls_lse[0]);
     }
 
     ofmatch->metadata = match->flow.metadata;
@@ -1559,13 +1560,11 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         if (error) {
             return error;
         }
-        fm->out_group = ntohl(ofm->out_group);
 
-        if ((ofm->command == OFPFC_DELETE
-             || ofm->command == OFPFC_DELETE_STRICT)
-            && ofm->out_group != htonl(OFPG_ANY)) {
-            return OFPERR_OFPFMFC_UNKNOWN;
-        }
+        fm->out_group = (ofm->command == OFPFC_DELETE ||
+                         ofm->command == OFPFC_DELETE_STRICT
+                         ? ntohl(ofm->out_group)
+                         : OFPG11_ANY);
         raw_flags = ofm->flags;
     } else {
         uint16_t command;
@@ -5346,7 +5345,7 @@ ofputil_normalize_match__(struct match *match, bool may_log)
         wc.masks.nd_target = in6addr_any;
     }
     if (!(may_match & MAY_MPLS)) {
-        wc.masks.mpls_lse = htonl(0);
+        memset(wc.masks.mpls_lse, 0, sizeof wc.masks.mpls_lse);
     }
 
     /* Log any changes. */
index 0d63841..003e554 100644 (file)
@@ -743,13 +743,13 @@ packet_update_csum128(struct ofpbuf *packet, uint8_t proto,
 
 static void
 packet_set_ipv6_addr(struct ofpbuf *packet, uint8_t proto,
-                     ovs_16aligned_be32 *addr, const ovs_be32 new_addr[4],
+                     ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4],
                      bool recalculate_csum)
 {
     if (recalculate_csum) {
         packet_update_csum128(packet, proto, addr, new_addr);
     }
-    memcpy(addr, new_addr, sizeof(*addr));
+    memcpy(addr, new_addr, sizeof(ovs_be32[4]));
 }
 
 static void
index 4e3e7db..fdff33c 100644 (file)
@@ -53,7 +53,7 @@ struct pcaprec_hdr {
 BUILD_ASSERT_DECL(sizeof(struct pcaprec_hdr) == 16);
 
 FILE *
-pcap_open(const char *file_name, const char *mode)
+ovs_pcap_open(const char *file_name, const char *mode)
 {
     struct stat s;
     FILE *file;
@@ -75,7 +75,7 @@ pcap_open(const char *file_name, const char *mode)
 
     switch (mode[0]) {
     case 'r':
-        error = pcap_read_header(file);
+        error = ovs_pcap_read_header(file);
         if (error) {
             errno = error;
             fclose(file);
@@ -84,12 +84,12 @@ pcap_open(const char *file_name, const char *mode)
         break;
 
     case 'w':
-        pcap_write_header(file);
+        ovs_pcap_write_header(file);
         break;
 
     case 'a':
         if (!fstat(fileno(file), &s) && !s.st_size) {
-            pcap_write_header(file);
+            ovs_pcap_write_header(file);
         }
         break;
 
@@ -100,7 +100,7 @@ pcap_open(const char *file_name, const char *mode)
 }
 
 int
-pcap_read_header(FILE *file)
+ovs_pcap_read_header(FILE *file)
 {
     struct pcap_hdr ph;
     if (fread(&ph, sizeof ph, 1, file) != 1) {
@@ -117,7 +117,7 @@ pcap_read_header(FILE *file)
 }
 
 void
-pcap_write_header(FILE *file)
+ovs_pcap_write_header(FILE *file)
 {
     /* The pcap reader is responsible for figuring out endianness based on the
      * magic number, so the lack of htonX calls here is intentional. */
@@ -133,7 +133,7 @@ pcap_write_header(FILE *file)
 }
 
 int
-pcap_read(FILE *file, struct ofpbuf **bufp, long long int *when)
+ovs_pcap_read(FILE *file, struct ofpbuf **bufp, long long int *when)
 {
     struct pcaprec_hdr prh;
     struct ofpbuf *buf;
@@ -190,7 +190,7 @@ pcap_read(FILE *file, struct ofpbuf **bufp, long long int *when)
 }
 
 void
-pcap_write(FILE *file, struct ofpbuf *buf)
+ovs_pcap_write(FILE *file, struct ofpbuf *buf)
 {
     struct pcaprec_hdr prh;
     struct timeval tv;
index ef491e5..5d79ccb 100644 (file)
@@ -23,11 +23,11 @@ struct flow;
 struct ofpbuf;
 
 /* PCAP file reading and writing. */
-FILE *pcap_open(const char *file_name, const char *mode);
-int pcap_read_header(FILE *);
-void pcap_write_header(FILE *);
-int pcap_read(FILE *, struct ofpbuf **, long long int *when);
-void pcap_write(FILE *, struct ofpbuf *);
+FILE *ovs_pcap_open(const char *file_name, const char *mode);
+int ovs_pcap_read_header(FILE *);
+void ovs_pcap_write_header(FILE *);
+int ovs_pcap_read(FILE *, struct ofpbuf **, long long int *when);
+void ovs_pcap_write(FILE *, struct ofpbuf *);
 \f
 /* Extracting TCP stream data from an Ethernet packet capture. */
 
diff --git a/lib/service-syn.man b/lib/service-syn.man
new file mode 100644 (file)
index 0000000..d029f22
--- /dev/null
@@ -0,0 +1,3 @@
+.IP "Service options:"
+[\fB\-\-service\fR]
+[\fB\-\-service\-monitor\fR]
diff --git a/lib/service.man b/lib/service.man
new file mode 100644 (file)
index 0000000..df36af9
--- /dev/null
@@ -0,0 +1,12 @@
+The following options are valid only on Windows platform.
+.TP
+\fB\-\-service\fR
+Causes \fB\*(PN\fR to run as a service in the background. The service
+should already have been created through external tools like \fBSC.exe\fR.
+.
+.TP
+\fB\-\-service\-monitor\fR
+Causes the \fB\*(PN\fR service to be automatically restarted by the Windows
+services manager if the service dies or exits for unexpected reasons.
+.IP
+When \fB\-\-service\fR is not specified, this option has no effect.
index bb48ade..24fc6fe 100644 (file)
@@ -1028,14 +1028,6 @@ xpipe_nonblocking(int fds[2])
     xset_nonblocking(fds[1]);
 }
 
-void
-xsocketpair(int domain, int type, int protocol, int fds[2])
-{
-    if (socketpair(domain, type, protocol, fds)) {
-        VLOG_FATAL("failed to create socketpair (%s)", ovs_strerror(errno));
-    }
-}
-
 static int
 getsockopt_int(int fd, int level, int option, const char *optname, int *valuep)
 {
@@ -1196,252 +1188,6 @@ describe_fd(int fd)
     return ds_steal_cstr(&string);
 }
 
-/* Returns the total of the 'iov_len' members of the 'n_iovs' in 'iovs'.
- * The caller must ensure that the total does not exceed SIZE_MAX. */
-size_t
-iovec_len(const struct iovec iovs[], size_t n_iovs)
-{
-    size_t len = 0;
-    size_t i;
-
-    for (i = 0; i < n_iovs; i++) {
-        len += iovs[i].iov_len;
-    }
-    return len;
-}
-
-/* Returns true if all of the 'n_iovs' iovecs in 'iovs' have length zero. */
-bool
-iovec_is_empty(const struct iovec iovs[], size_t n_iovs)
-{
-    size_t i;
-
-    for (i = 0; i < n_iovs; i++) {
-        if (iovs[i].iov_len) {
-            return false;
-        }
-    }
-    return true;
-}
-
-/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
- * in 'fds' on Unix domain socket 'sock'.  Returns the number of bytes
- * successfully sent or -1 if an error occurred.  On error, sets errno
- * appropriately.  */
-int
-send_iovec_and_fds(int sock,
-                   const struct iovec *iovs, size_t n_iovs,
-                   const int fds[], size_t n_fds)
-{
-    ovs_assert(sock >= 0);
-    if (n_fds > 0) {
-        union {
-            struct cmsghdr cm;
-            char control[CMSG_SPACE(SOUTIL_MAX_FDS * sizeof *fds)];
-        } cmsg;
-        struct msghdr msg;
-
-        ovs_assert(!iovec_is_empty(iovs, n_iovs));
-        ovs_assert(n_fds <= SOUTIL_MAX_FDS);
-
-        memset(&cmsg, 0, sizeof cmsg);
-        cmsg.cm.cmsg_len = CMSG_LEN(n_fds * sizeof *fds);
-        cmsg.cm.cmsg_level = SOL_SOCKET;
-        cmsg.cm.cmsg_type = SCM_RIGHTS;
-        memcpy(CMSG_DATA(&cmsg.cm), fds, n_fds * sizeof *fds);
-
-        msg.msg_name = NULL;
-        msg.msg_namelen = 0;
-        msg.msg_iov = CONST_CAST(struct iovec *, iovs);
-        msg.msg_iovlen = n_iovs;
-        msg.msg_control = &cmsg.cm;
-        msg.msg_controllen = CMSG_SPACE(n_fds * sizeof *fds);
-        msg.msg_flags = 0;
-
-        return sendmsg(sock, &msg, 0);
-    } else {
-        return writev(sock, iovs, n_iovs);
-    }
-}
-
-/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
- * in 'fds' on Unix domain socket 'sock'.  If 'skip_bytes' is nonzero, then the
- * first 'skip_bytes' of data in the iovecs are not sent, and none of the file
- * descriptors are sent.  The function continues to retry sending until an
- * error (other than EINTR) occurs or all the data and fds are sent.
- *
- * Returns 0 if all the data and fds were successfully sent, otherwise a
- * positive errno value.  Regardless of success, stores the number of bytes
- * sent (always at least 'skip_bytes') in '*bytes_sent'.  (If at least one byte
- * is sent, then all the fds have been sent.)
- *
- * 'skip_bytes' must be less than or equal to iovec_len(iovs, n_iovs). */
-int
-send_iovec_and_fds_fully(int sock,
-                         const struct iovec iovs[], size_t n_iovs,
-                         const int fds[], size_t n_fds,
-                         size_t skip_bytes, size_t *bytes_sent)
-{
-    *bytes_sent = 0;
-    while (n_iovs > 0) {
-        int retval;
-
-        if (skip_bytes) {
-            retval = skip_bytes;
-            skip_bytes = 0;
-        } else if (!*bytes_sent) {
-            retval = send_iovec_and_fds(sock, iovs, n_iovs, fds, n_fds);
-        } else {
-            retval = writev(sock, iovs, n_iovs);
-        }
-
-        if (retval > 0) {
-            *bytes_sent += retval;
-            while (retval > 0) {
-                const uint8_t *base = iovs->iov_base;
-                size_t len = iovs->iov_len;
-
-                if (retval < len) {
-                    size_t sent;
-                    int error;
-
-                    error = write_fully(sock, base + retval, len - retval,
-                                        &sent);
-                    *bytes_sent += sent;
-                    retval += sent;
-                    if (error) {
-                        return error;
-                    }
-                }
-                retval -= len;
-                iovs++;
-                n_iovs--;
-            }
-        } else if (retval == 0) {
-            if (iovec_is_empty(iovs, n_iovs)) {
-                break;
-            }
-            VLOG_WARN("send returned 0");
-            return EPROTO;
-        } else if (errno != EINTR) {
-            return errno;
-        }
-    }
-
-    return 0;
-}
-
-/* Sends the 'n_iovs' iovecs of data in 'iovs' and the 'n_fds' file descriptors
- * in 'fds' on Unix domain socket 'sock'.  The function continues to retry
- * sending until an error (other than EAGAIN or EINTR) occurs or all the data
- * and fds are sent.  Upon EAGAIN, the function blocks until the socket is
- * ready for more data.
- *
- * Returns 0 if all the data and fds were successfully sent, otherwise a
- * positive errno value. */
-int
-send_iovec_and_fds_fully_block(int sock,
-                               const struct iovec iovs[], size_t n_iovs,
-                               const int fds[], size_t n_fds)
-{
-    size_t sent = 0;
-
-    for (;;) {
-        int error;
-
-        error = send_iovec_and_fds_fully(sock, iovs, n_iovs,
-                                         fds, n_fds, sent, &sent);
-        if (error != EAGAIN) {
-            return error;
-        }
-        poll_fd_wait(sock, POLLOUT);
-        poll_block();
-    }
-}
-
-/* Attempts to receive from Unix domain socket 'sock' up to 'size' bytes of
- * data into 'data' and up to SOUTIL_MAX_FDS file descriptors into 'fds'.
- *
- *      - Upon success, returns the number of bytes of data copied into 'data'
- *        and stores the number of received file descriptors into '*n_fdsp'.
- *
- *      - On failure, returns a negative errno value and stores 0 in
- *        '*n_fdsp'.
- *
- *      - On EOF, returns 0 and stores 0 in '*n_fdsp'. */
-int
-recv_data_and_fds(int sock,
-                  void *data, size_t size,
-                  int fds[SOUTIL_MAX_FDS], size_t *n_fdsp)
-{
-    union {
-        struct cmsghdr cm;
-        char control[CMSG_SPACE(SOUTIL_MAX_FDS * sizeof *fds)];
-    } cmsg;
-    struct msghdr msg;
-    int retval;
-    struct cmsghdr *p;
-    size_t i;
-
-    *n_fdsp = 0;
-
-    do {
-        struct iovec iov;
-
-        iov.iov_base = data;
-        iov.iov_len = size;
-
-        msg.msg_name = NULL;
-        msg.msg_namelen = 0;
-        msg.msg_iov = &iov;
-        msg.msg_iovlen = 1;
-        msg.msg_control = &cmsg.cm;
-        msg.msg_controllen = sizeof cmsg.control;
-        msg.msg_flags = 0;
-
-        retval = recvmsg(sock, &msg, 0);
-    } while (retval < 0 && errno == EINTR);
-    if (retval <= 0) {
-        return retval < 0 ? -errno : 0;
-    }
-
-    for (p = CMSG_FIRSTHDR(&msg); p; p = CMSG_NXTHDR(&msg, p)) {
-        if (p->cmsg_level != SOL_SOCKET || p->cmsg_type != SCM_RIGHTS) {
-            VLOG_ERR("unexpected control message %d:%d",
-                     p->cmsg_level, p->cmsg_type);
-            goto error;
-        } else if (*n_fdsp) {
-            VLOG_ERR("multiple SCM_RIGHTS received");
-            goto error;
-        } else {
-            size_t n_fds = (p->cmsg_len - CMSG_LEN(0)) / sizeof *fds;
-            const int *fds_data = ALIGNED_CAST(const int *, CMSG_DATA(p));
-
-            ovs_assert(n_fds > 0);
-            if (n_fds > SOUTIL_MAX_FDS) {
-                VLOG_ERR("%"PRIuSIZE" fds received but only %d supported",
-                         n_fds, SOUTIL_MAX_FDS);
-                for (i = 0; i < n_fds; i++) {
-                    close(fds_data[i]);
-                }
-                goto error;
-            }
-
-            *n_fdsp = n_fds;
-            memcpy(fds, fds_data, n_fds * sizeof *fds);
-        }
-    }
-
-    return retval;
-
-error:
-    for (i = 0; i < *n_fdsp; i++) {
-        close(fds[i]);
-    }
-    *n_fdsp = 0;
-    return EPROTO;
-}
-
 /* Calls ioctl() on an AF_INET sock, passing the specified 'command' and
  * 'arg'.  Returns 0 if successful, otherwise a positive errno value. */
 int
index 670eeb3..d5b44b0 100644 (file)
@@ -73,30 +73,6 @@ char *describe_fd(int fd);
  * in <netinet/ip.h> is used. */
 #define DSCP_DEFAULT (IPTOS_PREC_INTERNETCONTROL >> 2)
 
-/* Maximum number of fds that we support sending or receiving at one time
- * across a Unix domain socket. */
-#define SOUTIL_MAX_FDS 8
-
-/* Iovecs. */
-size_t iovec_len(const struct iovec *iovs, size_t n_iovs);
-bool iovec_is_empty(const struct iovec *iovs, size_t n_iovs);
-
-/* Functions particularly useful for Unix domain sockets. */
-void xsocketpair(int domain, int type, int protocol, int fds[2]);
-int send_iovec_and_fds(int sock,
-                       const struct iovec *iovs, size_t n_iovs,
-                       const int fds[], size_t n_fds);
-int send_iovec_and_fds_fully(int sock,
-                             const struct iovec *iovs, size_t n_iovs,
-                             const int fds[], size_t n_fds,
-                             size_t skip_bytes, size_t *bytes_sent);
-int send_iovec_and_fds_fully_block(int sock,
-                                   const struct iovec *iovs, size_t n_iovs,
-                                   const int fds[], size_t n_fds);
-int recv_data_and_fds(int sock,
-                      void *data, size_t size,
-                      int fds[SOUTIL_MAX_FDS], size_t *n_fdsp);
-
 /* Helpers for calling ioctl() on an AF_INET socket. */
 struct ifreq;
 int af_inet_ioctl(unsigned long int command, const void *arg);
index 0ebf085..845f86c 100644 (file)
@@ -376,6 +376,10 @@ set_program_name__(const char *argv0, const char *version, const char *date,
 #ifdef _WIN32
     char *basename;
     size_t max_len = strlen(argv0) + 1;
+
+    if (program_name) {
+        return;
+    }
     basename = xmalloc(max_len);
     _splitpath_s(argv0, NULL, 0, NULL, 0, basename, max_len, NULL, 0);
     assert_single_threaded();
@@ -1649,3 +1653,19 @@ exit:
     return ok;
 }
 
+#ifdef _WIN32
+\f
+/* Calls FormatMessage() with GetLastError() as an argument. Returns
+ * pointer to a buffer that receives the null-terminated string that specifies
+ * the formatted message and that has to be freed by the caller with
+ * LocalFree(). */
+char *
+ovs_lasterror_to_string(void)
+{
+    char *buffer;
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
+                  | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0,
+                  (char *)&buffer, 0, NULL);
+    return buffer;
+}
+#endif
index 8886a54..2e3d1da 100644 (file)
@@ -490,6 +490,11 @@ void bitwise_put(uint64_t value,
 uint64_t bitwise_get(const void *src, unsigned int src_len,
                      unsigned int src_ofs, unsigned int n_bits);
 
+#ifdef _WIN32
+\f
+char *ovs_lasterror_to_string(void);
+#endif
+
 #ifdef  __cplusplus
 }
 #endif
index 4ead3e2..1890dc2 100644 (file)
@@ -21,6 +21,10 @@ module.
 \fBsyslog\fR, \fBconsole\fR, or \fBfile\fR, to limit the log level
 change to only to the system log, to the console, or to a file,
 respectively.
+.IP
+On Windows platform, \fBsyslog\fR is accepted as a word and is only
+useful along with the \fB\-\-syslog\-target\fR option (the word has no
+effect otherwise).
 .
 .IP \(bu 
 \fBoff\fR, \fBemer\fR, \fBerr\fR, \fBwarn\fR, \fBinfo\fR, or
index 540e72a..42e4869 100644 (file)
@@ -592,46 +592,52 @@ vlog_disable_rate_limit(struct unixctl_conn *conn, int argc,
     set_rate_limits(conn, argc, argv, false);
 }
 
-static void
-vlog_init__(void)
-{
-    static char *program_name_copy;
-    long long int now;
-
-    /* openlog() is allowed to keep the pointer passed in, without making a
-     * copy.  The daemonize code sometimes frees and replaces 'program_name',
-     * so make a private copy just for openlog().  (We keep a pointer to the
-     * private copy to suppress memory leak warnings in case openlog() does
-     * make its own copy.) */
-    program_name_copy = program_name ? xstrdup(program_name) : NULL;
-    openlog(program_name_copy, LOG_NDELAY, LOG_DAEMON);
-
-    now = time_wall_msec();
-    if (now < 0) {
-        char *s = xastrftime_msec("%a, %d %b %Y %H:%M:%S", now, true);
-        VLOG_ERR("current time is negative: %s (%lld)", s, now);
-        free(s);
-    }
-
-    unixctl_command_register(
-        "vlog/set", "{spec | PATTERN:facility:pattern}",
-        1, INT_MAX, vlog_unixctl_set, NULL);
-    unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list, NULL);
-    unixctl_command_register("vlog/enable-rate-limit", "[module]...",
-                             0, INT_MAX, vlog_enable_rate_limit, NULL);
-    unixctl_command_register("vlog/disable-rate-limit", "[module]...",
-                             0, INT_MAX, vlog_disable_rate_limit, NULL);
-    unixctl_command_register("vlog/reopen", "", 0, 0,
-                             vlog_unixctl_reopen, NULL);
-}
-
 /* Initializes the logging subsystem and registers its unixctl server
  * commands. */
 void
 vlog_init(void)
 {
-    static pthread_once_t once = PTHREAD_ONCE_INIT;
-    pthread_once(&once, vlog_init__);
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+
+    if (ovsthread_once_start(&once)) {
+        static char *program_name_copy;
+        long long int now;
+
+        /* Do initialization work that needs to be done before any logging
+         * occurs.  We want to keep this really minimal because any attempt to
+         * log anything before calling ovsthread_once_done() will deadlock. */
+
+        /* openlog() is allowed to keep the pointer passed in, without making a
+         * copy.  The daemonize code sometimes frees and replaces
+         * 'program_name', so make a private copy just for openlog().  (We keep
+         * a pointer to the private copy to suppress memory leak warnings in
+         * case openlog() does make its own copy.) */
+        program_name_copy = program_name ? xstrdup(program_name) : NULL;
+        openlog(program_name_copy, LOG_NDELAY, LOG_DAEMON);
+        ovsthread_once_done(&once);
+
+        /* Now do anything that we want to happen only once but doesn't have to
+         * finish before we start logging. */
+
+        now = time_wall_msec();
+        if (now < 0) {
+            char *s = xastrftime_msec("%a, %d %b %Y %H:%M:%S", now, true);
+            VLOG_ERR("current time is negative: %s (%lld)", s, now);
+            free(s);
+        }
+
+        unixctl_command_register(
+            "vlog/set", "{spec | PATTERN:facility:pattern}",
+            1, INT_MAX, vlog_unixctl_set, NULL);
+        unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list,
+                                 NULL);
+        unixctl_command_register("vlog/enable-rate-limit", "[module]...",
+                                 0, INT_MAX, vlog_enable_rate_limit, NULL);
+        unixctl_command_register("vlog/disable-rate-limit", "[module]...",
+                                 0, INT_MAX, vlog_disable_rate_limit, NULL);
+        unixctl_command_register("vlog/reopen", "", 0, 0,
+                                 vlog_unixctl_reopen, NULL);
+    }
 }
 
 /* Enables VLF_FILE log output to be written asynchronously to disk.
index f675a4b..2ccef7f 100644 (file)
@@ -21,6 +21,10 @@ module.
 \fBsyslog\fR, \fBconsole\fR, or \fBfile\fR, to limit the log level
 change to only to the system log, to the console, or to a file,
 respectively.
+.IP
+On Windows platform, \fBsyslog\fR is accepted as a word and is only
+useful along with the \fB\-\-syslog\-target\fR option (the word has no
+effect otherwise).
 .
 .IP \(bu 
 \fBoff\fR, \fBemer\fR, \fBerr\fR, \fBwarn\fR, \fBinfo\fR, or
index 2d543e6..7846fb0 100644 (file)
@@ -79,7 +79,11 @@ AC_DEFUN([AX_CHECK_OPENSSL], [
             if test -f "$ssldir/include/openssl/ssl.h"; then
                 SSL_INCLUDES="-I$ssldir/include"
                 SSL_LDFLAGS="-L$ssldir/lib"
-                SSL_LIBS="-lssl -lcrypto"
+                if test "$WIN32" = "yes"; then
+                    SSL_LIBS="-lssleay32 -llibeay32"
+                else
+                    SSL_LIBS="-lssl -lcrypto"
+                fi
                 found=true
                 AC_MSG_RESULT([yes])
                 break
@@ -106,7 +110,8 @@ AC_DEFUN([AX_CHECK_OPENSSL], [
     LIBS="$SSL_LIBS $LIBS"
     CPPFLAGS="$SSL_INCLUDES $CPPFLAGS"
     AC_LINK_IFELSE(
-        [AC_LANG_PROGRAM([#include <openssl/ssl.h>], [SSL_new(NULL)])],
+        [AC_LANG_PROGRAM([#include <openssl/ssl.h>],
+            [SSL_CTX *ctx=NULL;SSL_new(ctx)])],
         [
             AC_MSG_RESULT([yes])
             $1
index c0af6d4..7a0465b 100644 (file)
@@ -75,7 +75,6 @@ AC_DEFUN([OVS_CHECK_NETLINK],
                    [HAVE_NETLINK=yes],
                    [HAVE_NETLINK=no],
                    [#include <sys/socket.h>
-   #include <linux/types.h>
    ])
    AM_CONDITIONAL([HAVE_NETLINK], [test "$HAVE_NETLINK" = yes])
    if test "$HAVE_NETLINK" = yes; then
index 7f59019..5d5df03 100644 (file)
@@ -38,6 +38,8 @@ ovsdb/ovsdb-server.1: \
        lib/daemon-syn.man \
        lib/daemon.man \
        lib/memory-unixctl.man \
+       lib/service-syn.man \
+       lib/service.man \
        lib/ssl-bootstrap-syn.man \
        lib/ssl-bootstrap.man \
        lib/ssl-syn.man \
@@ -56,6 +58,8 @@ lib/coverage-unixctl.man:
 lib/daemon-syn.man:
 lib/daemon.man:
 lib/memory-unixctl.man:
+lib/service-syn.man:
+lib/service.man:
 lib/ssl-bootstrap-syn.man:
 lib/ssl-bootstrap.man:
 lib/ssl-syn.man:
@@ -232,6 +236,7 @@ vswitchd/ovs-vswitchd.8: \
        lib/coverage-unixctl.man \
        lib/daemon.man \
        lib/memory-unixctl.man \
+       lib/service.man \
        lib/ssl-bootstrap.man \
        lib/ssl.man \
        lib/vlog-unixctl.man \
@@ -245,6 +250,7 @@ lib/common.man:
 lib/coverage-unixctl.man:
 lib/daemon.man:
 lib/memory-unixctl.man:
+lib/service.man:
 lib/ssl-bootstrap.man:
 lib/ssl.man:
 lib/vlog-unixctl.man:
index 1e0c12c..489012a 100644 (file)
@@ -41,6 +41,7 @@
 #define MAX_QUEUE_LENGTH 512
 #define FLOW_MISS_MAX_BATCH 50
 #define REVALIDATE_MAX_BATCH 50
+#define MAX_IDLE 1500
 
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif_upcall);
 
@@ -125,8 +126,12 @@ struct udpif {
     unsigned int avg_n_flows;
 
     /* Following fields are accessed and modified by different threads. */
-    atomic_llong max_idle;             /* Maximum datapath flow idle time. */
     atomic_uint flow_limit;            /* Datapath flow hard limit. */
+
+    /* n_flows_mutex prevents multiple threads updating these concurrently. */
+    atomic_uint64_t n_flows;           /* Number of flows in the datapath. */
+    atomic_llong n_flows_timestamp;    /* Last time n_flows was updated. */
+    struct ovs_mutex n_flows_mutex;
 };
 
 enum upcall_type {
@@ -197,7 +202,6 @@ struct flow_miss {
     struct ofproto_dpif *ofproto;
 
     struct flow flow;
-    enum odp_key_fitness key_fitness;
     const struct nlattr *key;
     size_t key_len;
     enum dpif_upcall_type upcall_type;
@@ -223,7 +227,7 @@ static void *udpif_flow_dumper(void *);
 static void *udpif_dispatcher(void *);
 static void *udpif_upcall_handler(void *);
 static void *udpif_revalidator(void *);
-static uint64_t udpif_get_n_flows(const struct udpif *);
+static uint64_t udpif_get_n_flows(struct udpif *);
 static void revalidate_udumps(struct revalidator *, struct list *udumps);
 static void revalidator_sweep(struct revalidator *);
 static void upcall_unixctl_show(struct unixctl_conn *conn, int argc,
@@ -254,13 +258,15 @@ udpif_create(struct dpif_backer *backer, struct dpif *dpif)
 
     udpif->dpif = dpif;
     udpif->backer = backer;
-    atomic_init(&udpif->max_idle, 5000);
     atomic_init(&udpif->flow_limit, MIN(ofproto_flow_limit, 10000));
     udpif->secret = random_uint32();
     udpif->reval_seq = seq_create();
     udpif->dump_seq = seq_create();
     latch_init(&udpif->exit_latch);
     list_push_back(&all_udpifs, &udpif->list_node);
+    atomic_init(&udpif->n_flows, 0);
+    atomic_init(&udpif->n_flows_timestamp, LLONG_MIN);
+    ovs_mutex_init(&udpif->n_flows_mutex);
 
     return udpif;
 }
@@ -275,8 +281,10 @@ udpif_destroy(struct udpif *udpif)
     latch_destroy(&udpif->exit_latch);
     seq_destroy(udpif->reval_seq);
     seq_destroy(udpif->dump_seq);
-    atomic_destroy(&udpif->max_idle);
     atomic_destroy(&udpif->flow_limit);
+    atomic_destroy(&udpif->n_flows);
+    atomic_destroy(&udpif->n_flows_timestamp);
+    ovs_mutex_destroy(&udpif->n_flows_mutex);
     free(udpif);
 }
 
@@ -470,12 +478,25 @@ upcall_destroy(struct upcall *upcall)
 }
 
 static uint64_t
-udpif_get_n_flows(const struct udpif *udpif)
+udpif_get_n_flows(struct udpif *udpif)
 {
-    struct dpif_dp_stats stats;
-
-    dpif_get_dp_stats(udpif->dpif, &stats);
-    return stats.n_flows;
+    long long int time, now;
+    uint64_t flow_count;
+
+    now = time_msec();
+    atomic_read(&udpif->n_flows_timestamp, &time);
+    if (time < now - 100 && !ovs_mutex_trylock(&udpif->n_flows_mutex)) {
+        struct dpif_dp_stats stats;
+
+        atomic_store(&udpif->n_flows_timestamp, now);
+        dpif_get_dp_stats(udpif->dpif, &stats);
+        flow_count = stats.n_flows;
+        atomic_store(&udpif->n_flows, flow_count);
+        ovs_mutex_unlock(&udpif->n_flows_mutex);
+    } else {
+        atomic_read(&udpif->n_flows, &flow_count);
+    }
+    return flow_count;
 }
 
 /* The dispatcher thread is responsible for receiving upcalls from the kernel,
@@ -509,7 +530,6 @@ udpif_flow_dumper(void *arg)
         struct dpif_flow_dump dump;
         size_t key_len, mask_len;
         unsigned int flow_limit;
-        long long int max_idle;
         bool need_revalidate;
         uint64_t reval_seq;
         size_t n_flows, i;
@@ -522,18 +542,6 @@ udpif_flow_dumper(void *arg)
         udpif->max_n_flows = MAX(n_flows, udpif->max_n_flows);
         udpif->avg_n_flows = (udpif->avg_n_flows + n_flows) / 2;
 
-        atomic_read(&udpif->flow_limit, &flow_limit);
-        if (n_flows < flow_limit / 8) {
-            max_idle = 5000;
-        } else if (n_flows < flow_limit / 4) {
-            max_idle = 2000;
-        } else if (n_flows < flow_limit / 2) {
-            max_idle = 1000;
-        } else {
-            max_idle = 500;
-        }
-        atomic_store(&udpif->max_idle, max_idle);
-
         start_time = time_msec();
         dpif_flow_dump_start(&dump, udpif->dpif);
         while (dpif_flow_dump_next(&dump, &key, &key_len, &mask, &mask_len,
@@ -591,8 +599,9 @@ udpif_flow_dumper(void *arg)
             ovs_mutex_unlock(&revalidator->mutex);
         }
 
-        duration = time_msec() - start_time;
+        duration = MAX(time_msec() - start_time, 1);
         udpif->dump_duration = duration;
+        atomic_read(&udpif->flow_limit, &flow_limit);
         if (duration > 2000) {
             flow_limit /= duration / 1000;
         } else if (duration > 1300) {
@@ -609,7 +618,7 @@ udpif_flow_dumper(void *arg)
                       duration);
         }
 
-        poll_timer_wait_until(start_time + MIN(max_idle, 500));
+        poll_timer_wait_until(start_time + MIN(MAX_IDLE, 500));
         seq_wait(udpif->reval_seq, udpif->last_reval_seq);
         latch_wait(&udpif->exit_latch);
         poll_block();
@@ -931,7 +940,7 @@ handle_upcalls(struct handler *handler, struct list *upcalls)
         int error;
 
         error = xlate_receive(udpif->backer, packet, dupcall->key,
-                              dupcall->key_len, &flow, &miss->key_fitness,
+                              dupcall->key_len, &flow,
                               &ofproto, &ipfix, &sflow, NULL, &odp_in_port);
         if (error) {
             if (error == ENODEV) {
@@ -1129,8 +1138,11 @@ handle_upcalls(struct handler *handler, struct list *upcalls)
             atomic_read(&enable_megaflows, &megaflow);
             ofpbuf_use_stack(&mask, &miss->mask_buf, sizeof miss->mask_buf);
             if (megaflow) {
+                size_t max_mpls;
+
+                max_mpls = ofproto_dpif_get_max_mpls_depth(miss->ofproto);
                 odp_flow_key_from_mask(&mask, &miss->xout.wc.masks,
-                                       &miss->flow, UINT32_MAX);
+                                       &miss->flow, UINT32_MAX, max_mpls);
             }
 
             op = &ops[n_ops++];
@@ -1290,7 +1302,7 @@ revalidate_ukey(struct udpif *udpif, struct udpif_flow_dump *udump,
     }
 
     error = xlate_receive(udpif->backer, NULL, ukey->key, ukey->key_len, &flow,
-                          NULL, &ofproto, NULL, NULL, NULL, &odp_in_port);
+                          &ofproto, NULL, NULL, NULL, &odp_in_port);
     if (error) {
         goto exit;
     }
@@ -1362,12 +1374,12 @@ revalidate_udumps(struct revalidator *revalidator, struct list *udumps)
     long long int max_idle;
     bool must_del;
 
-    atomic_read(&udpif->max_idle, &max_idle);
     atomic_read(&udpif->flow_limit, &flow_limit);
 
     n_flows = udpif_get_n_flows(udpif);
 
     must_del = false;
+    max_idle = MAX_IDLE;
     if (n_flows > flow_limit) {
         must_del = n_flows > 2 * flow_limit;
         max_idle = 100;
@@ -1453,7 +1465,7 @@ revalidate_udumps(struct revalidator *revalidator, struct list *udumps)
             struct flow flow;
 
             if (!xlate_receive(udpif->backer, NULL, ops[i].op.u.flow_del.key,
-                               ops[i].op.u.flow_del.key_len, &flow, NULL,
+                               ops[i].op.u.flow_del.key_len, &flow,
                                &ofproto, NULL, NULL, &netflow, NULL)) {
                 struct xlate_in xin;
 
@@ -1502,17 +1514,14 @@ upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
 
     LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
         unsigned int flow_limit;
-        long long int max_idle;
         size_t i;
 
         atomic_read(&udpif->flow_limit, &flow_limit);
-        atomic_read(&udpif->max_idle, &max_idle);
 
         ds_put_format(&ds, "%s:\n", dpif_name(udpif->dpif));
         ds_put_format(&ds, "\tflows         : (current %"PRIu64")"
             " (avg %u) (max %u) (limit %u)\n", udpif_get_n_flows(udpif),
             udpif->avg_n_flows, udpif->max_n_flows, flow_limit);
-        ds_put_format(&ds, "\tmax idle      : %lldms\n", max_idle);
         ds_put_format(&ds, "\tdump duration : %lldms\n", udpif->dump_duration);
 
         ds_put_char(&ds, '\n');
index c5e6600..ad44582 100644 (file)
@@ -51,6 +51,7 @@
 
 COVERAGE_DEFINE(xlate_actions);
 COVERAGE_DEFINE(xlate_actions_oversize);
+COVERAGE_DEFINE(xlate_actions_mpls_overflow);
 
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
 
@@ -92,6 +93,10 @@ struct xbridge {
      * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions.
      * False if the datapath supports only 8-byte (or shorter) userdata. */
     bool variable_length_userdata;
+
+    /* Number of MPLS label stack entries that the datapath supports
+     * in matches. */
+    size_t max_mpls_depth;
 };
 
 struct xbundle {
@@ -170,13 +175,6 @@ struct xlate_ctx {
     /* The rule that we are currently translating, or NULL. */
     struct rule_dpif *rule;
 
-    int mpls_depth_delta;       /* Delta of the mpls stack depth since
-                                 * actions were last committed.
-                                 * Must be between -1 and 1 inclusive. */
-    ovs_be32 pre_push_mpls_lse; /* Used to record the top-most MPLS LSE
-                                 * prior to an mpls_push so that it may be
-                                 * used for a subsequent mpls_pop. */
-
     /* Resubmit statistics, via xlate_table_action(). */
     int recurse;                /* Current resubmit nesting depth. */
     int resubmits;              /* Total number of resubmits. */
@@ -255,7 +253,8 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
                   const struct dpif_ipfix *ipfix,
                   const struct netflow *netflow, enum ofp_config_flags frag,
                   bool forward_bpdu, bool has_in_band,
-                  bool variable_length_userdata)
+                  bool variable_length_userdata,
+                  size_t max_mpls_depth)
 {
     struct xbridge *xbridge = xbridge_lookup(ofproto);
 
@@ -308,6 +307,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
     xbridge->miss_rule = miss_rule;
     xbridge->no_packet_in_rule = no_packet_in_rule;
     xbridge->variable_length_userdata = variable_length_userdata;
+    xbridge->max_mpls_depth = max_mpls_depth;
 }
 
 void
@@ -520,12 +520,10 @@ xlate_ofport_remove(struct ofport_dpif *ofport)
 
 /* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key'
  * respectively), populates 'flow' with the result of odp_flow_key_to_flow().
- * Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as
- * returned by odp_flow_key_to_flow().  Also, optionally populates 'ofproto'
- * with the ofproto_dpif, 'odp_in_port' with the datapath in_port, that
- * 'packet' ingressed, and 'ipfix', 'sflow', and 'netflow' with the appropriate
- * handles for those protocols if they're enabled.  Caller is responsible for
- * unrefing them.
+ * Optionally populates 'ofproto' with the ofproto_dpif, 'odp_in_port' with
+ * the datapath in_port, that 'packet' ingressed, and 'ipfix', 'sflow', and
+ * 'netflow' with the appropriate handles for those protocols if they're
+ * enabled.  Caller is responsible for unrefing them.
  *
  * If 'ofproto' is nonnull, requires 'flow''s in_port to exist.  Otherwise sets
  * 'flow''s in_port to OFPP_NONE.
@@ -545,19 +543,16 @@ xlate_ofport_remove(struct ofport_dpif *ofport)
  * or some other positive errno if there are other problems. */
 int
 xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
-              const struct nlattr *key, size_t key_len,
-              struct flow *flow, enum odp_key_fitness *fitnessp,
+              const struct nlattr *key, size_t key_len, struct flow *flow,
               struct ofproto_dpif **ofproto, struct dpif_ipfix **ipfix,
               struct dpif_sflow **sflow, struct netflow **netflow,
               odp_port_t *odp_in_port)
 {
-    enum odp_key_fitness fitness;
     const struct xport *xport;
     int error = ENODEV;
 
     ovs_rwlock_rdlock(&xlate_rwlock);
-    fitness = odp_flow_key_to_flow(key, key_len, flow);
-    if (fitness == ODP_FIT_ERROR) {
+    if (odp_flow_key_to_flow(key, key_len, flow) == ODP_FIT_ERROR) {
         error = EINVAL;
         goto exit;
     }
@@ -583,8 +578,6 @@ xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
              * vlan_tci if it is called on 'packet'. */
             eth_push_vlan(packet, htons(ETH_TYPE_VLAN), flow->vlan_tci);
         }
-        /* We can't reproduce 'key' from 'flow'. */
-        fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
     }
     error = 0;
 
@@ -605,9 +598,6 @@ xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
     }
 
 exit:
-    if (fitnessp) {
-        *fitnessp = fitness;
-    }
     ovs_rwlock_unlock(&xlate_rwlock);
     return error;
 }
@@ -1694,7 +1684,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
 
     /* If 'struct flow' gets additional metadata, we'll need to zero it out
      * before traversing a patch port. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     if (!xport) {
         xlate_report(ctx, "Nonexistent output port");
@@ -1809,8 +1799,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     if (out_port != ODPP_NONE) {
         ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
                                               &ctx->xout->odp_actions,
-                                              &ctx->xout->wc,
-                                              &ctx->mpls_depth_delta);
+                                              &ctx->xout->wc);
         nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
                             out_port);
 
@@ -2085,8 +2074,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
 
     ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                           &ctx->xout->odp_actions,
-                                          &ctx->xout->wc,
-                                          &ctx->mpls_depth_delta);
+                                          &ctx->xout->wc);
 
     odp_execute_actions(NULL, packet, &md, ctx->xout->odp_actions.data,
                         ctx->xout->odp_actions.size, NULL);
@@ -2110,99 +2098,63 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
     ofpbuf_delete(packet);
 }
 
-static bool
-compose_mpls_push_action(struct xlate_ctx *ctx, ovs_be16 eth_type)
+static void
+compose_mpls_push_action(struct xlate_ctx *ctx, struct ofpact_push_mpls *mpls)
 {
     struct flow_wildcards *wc = &ctx->xout->wc;
     struct flow *flow = &ctx->xin->flow;
+    ovs_be16 vlan_tci = flow->vlan_tci;
+    int n;
 
-    ovs_assert(eth_type_mpls(eth_type));
-
-    /* If mpls_depth_delta is negative then an MPLS POP action has been
-     * composed and the resulting MPLS label stack is unknown.  This means
-     * an MPLS PUSH action can't be composed as it needs to know either the
-     * top-most MPLS LSE to use as a template for the new MPLS LSE, or that
-     * there is no MPLS label stack present.  Thus, stop processing.
-     *
-     * If mpls_depth_delta is positive then an MPLS PUSH action has been
-     * composed and no further MPLS PUSH action may be performed without
-     * losing MPLS LSE and ether type information held in xtx->xin->flow.
-     * Thus, stop processing.
-     *
-     * If the MPLS LSE of the flow and base_flow differ then the MPLS LSE
-     * has been updated.  Performing a MPLS PUSH action may be would result in
-     * losing MPLS LSE and ether type information held in xtx->xin->flow.
-     * Thus, stop processing.
-     *
-     * It is planned that in the future this case will be handled
-     * by recirculation */
-    if (ctx->mpls_depth_delta ||
-        ctx->xin->flow.mpls_lse != ctx->base_flow.mpls_lse) {
-        return true;
-    }
-
-    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+    ovs_assert(eth_type_mpls(mpls->ethertype));
 
-    ctx->pre_push_mpls_lse = ctx->xin->flow.mpls_lse;
-
-    if (eth_type_mpls(ctx->xin->flow.dl_type)) {
-        flow->mpls_lse &= ~htonl(MPLS_BOS_MASK);
-    } else {
-        ovs_be32 label;
-        uint8_t tc, ttl;
-
-        if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
-            label = htonl(0x2); /* IPV6 Explicit Null. */
+    n = flow_count_mpls_labels(flow, wc);
+    if (!n) {
+        if (mpls->position == OFPACT_MPLS_BEFORE_VLAN) {
+            vlan_tci = 0;
         } else {
-            label = htonl(0x0); /* IPV4 Explicit Null. */
+            flow->vlan_tci = 0;
+        }
+        ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
+                                              &ctx->xout->odp_actions,
+                                              &ctx->xout->wc);
+    } else if (n >= FLOW_MAX_MPLS_LABELS) {
+        if (ctx->xin->packet != NULL) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl, "bridge %s: dropping packet on which an "
+                         "MPLS push action can't be performed as it would "
+                         "have more MPLS LSEs than the %d supported.",
+                         ctx->xbridge->name, FLOW_MAX_MPLS_LABELS);
         }
-        wc->masks.nw_tos |= IP_DSCP_MASK;
-        wc->masks.nw_ttl = 0xff;
-        tc = (flow->nw_tos & IP_DSCP_MASK) >> 2;
-        ttl = flow->nw_ttl ? flow->nw_ttl : 0x40;
-        flow->mpls_lse = set_mpls_lse_values(ttl, tc, 1, label);
+        ctx->exit = true;
+        return;
+    } else if (n >= ctx->xbridge->max_mpls_depth) {
+        COVERAGE_INC(xlate_actions_mpls_overflow);
+        ctx->xout->slow |= SLOW_ACTION;
     }
-    flow->dl_type = eth_type;
-    ctx->mpls_depth_delta++;
 
-    return false;
+    flow_push_mpls(flow, n, mpls->ethertype, wc);
+    flow->vlan_tci = vlan_tci;
 }
 
-static bool
+static void
 compose_mpls_pop_action(struct xlate_ctx *ctx, ovs_be16 eth_type)
 {
     struct flow_wildcards *wc = &ctx->xout->wc;
+    struct flow *flow = &ctx->xin->flow;
+    int n = flow_count_mpls_labels(flow, wc);
 
-    if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
-        return true;
-    }
-
-    /* If mpls_depth_delta is negative then an MPLS POP action has been
-     * composed.  Performing another MPLS POP action
-     * would result in losing ether type that results from
-     * the already composed MPLS POP. Thus, stop processing.
-     *
-     * It is planned that in the future this case will be handled
-     * by recirculation */
-    if (ctx->mpls_depth_delta < 0) {
-        return true;
-    }
-
-    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
-
-    /* If mpls_depth_delta is positive then an MPLS PUSH action has been
-     * executed and the previous MPLS LSE saved in ctx->pre_push_mpls_lse. The
-     * flow's MPLS LSE should be restored to that value to allow any
-     * subsequent actions that update of the LSE to be executed correctly.
-     */
-    if (ctx->mpls_depth_delta > 0) {
-        ctx->xin->flow.mpls_lse = ctx->pre_push_mpls_lse;
+    if (!flow_pop_mpls(flow, n, eth_type, wc) && n >= FLOW_MAX_MPLS_LABELS) {
+        if (ctx->xin->packet != NULL) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl, "bridge %s: dropping packet on which an "
+                         "MPLS pop action can't be performed as it has "
+                         "more MPLS LSEs than the %d supported.",
+                         ctx->xbridge->name, FLOW_MAX_MPLS_LABELS);
+        }
+        ctx->exit = true;
+        ofpbuf_clear(&ctx->xout->odp_actions);
     }
-
-    ctx->xin->flow.dl_type = eth_type;
-    ctx->mpls_depth_delta--;
-
-    return false;
 }
 
 static bool
@@ -2231,99 +2183,53 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
     }
 }
 
-static bool
+static void
 compose_set_mpls_label_action(struct xlate_ctx *ctx, ovs_be32 label)
 {
-    if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
-        return true;
-    }
-
-    /* If mpls_depth_delta is negative then an MPLS POP action has been
-     * executed and the resulting MPLS label stack is unknown.  This means
-     * a SET MPLS LABEL action can't be executed as it needs to manipulate
-     * the top-most MPLS LSE. Thus, stop processing.
-     *
-     * It is planned that in the future this case will be handled
-     * by recirculation.
-     */
-    if (ctx->mpls_depth_delta < 0) {
-        return true;
+    if (eth_type_mpls(ctx->xin->flow.dl_type)) {
+        ctx->xout->wc.masks.mpls_lse[0] |= htonl(MPLS_LABEL_MASK);
+        set_mpls_lse_label(&ctx->xin->flow.mpls_lse[0], label);
     }
-
-    ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK);
-    set_mpls_lse_label(&ctx->xin->flow.mpls_lse, label);
-    return false;
 }
 
-static bool
+static void
 compose_set_mpls_tc_action(struct xlate_ctx *ctx, uint8_t tc)
 {
-    if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
-        return true;
-    }
-
-    /* If mpls_depth_delta is negative then an MPLS POP action has been
-     * executed and the resulting MPLS label stack is unknown.  This means
-     * a SET MPLS TC action can't be executed as it needs to manipulate
-     * the top-most MPLS LSE. Thus, stop processing.
-     *
-     * It is planned that in the future this case will be handled
-     * by recirculation.
-     */
-    if (ctx->mpls_depth_delta < 0) {
-        return true;
+    if (eth_type_mpls(ctx->xin->flow.dl_type)) {
+        ctx->xout->wc.masks.mpls_lse[0] |= htonl(MPLS_TC_MASK);
+        set_mpls_lse_tc(&ctx->xin->flow.mpls_lse[0], tc);
     }
-
-    ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK);
-    set_mpls_lse_tc(&ctx->xin->flow.mpls_lse, tc);
-    return false;
 }
 
-static bool
+static void
 compose_set_mpls_ttl_action(struct xlate_ctx *ctx, uint8_t ttl)
 {
-    if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
-        return true;
-    }
-
-    /* If mpls_depth_delta is negative then an MPLS POP action has been
-     * executed and the resulting MPLS label stack is unknown.  This means
-     * a SET MPLS TTL push action can't be executed as it needs to manipulate
-     * the top-most MPLS LSE. Thus, stop processing.
-     *
-     * It is planned that in the future this case will be handled
-     * by recirculation.
-     */
-    if (ctx->mpls_depth_delta < 0) {
-        return true;
+    if (eth_type_mpls(ctx->xin->flow.dl_type)) {
+        ctx->xout->wc.masks.mpls_lse[0] |= htonl(MPLS_TTL_MASK);
+        set_mpls_lse_ttl(&ctx->xin->flow.mpls_lse[0], ttl);
     }
-
-    ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_TTL_MASK);
-    set_mpls_lse_ttl(&ctx->xin->flow.mpls_lse, ttl);
-    return false;
 }
 
 static bool
 compose_dec_mpls_ttl_action(struct xlate_ctx *ctx)
 {
     struct flow *flow = &ctx->xin->flow;
-    uint8_t ttl = mpls_lse_to_ttl(flow->mpls_lse);
+    uint8_t ttl = mpls_lse_to_ttl(flow->mpls_lse[0]);
     struct flow_wildcards *wc = &ctx->xout->wc;
 
     memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+    if (eth_type_mpls(flow->dl_type)) {
+        if (ttl > 1) {
+            ttl--;
+            set_mpls_lse_ttl(&flow->mpls_lse[0], ttl);
+            return false;
+        } else {
+            execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
 
-    if (!eth_type_mpls(flow->dl_type)) {
-        return false;
-    }
-
-    if (ttl > 1) {
-        ttl--;
-        set_mpls_lse_ttl(&flow->mpls_lse, ttl);
-        return false;
+            /* Stop processing for current table. */
+            return true;
+        }
     } else {
-        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
-
-        /* Stop processing for current table. */
         return true;
     }
 }
@@ -2535,8 +2441,7 @@ xlate_sample_action(struct xlate_ctx *ctx,
 
   ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                         &ctx->xout->odp_actions,
-                                        &ctx->xout->wc,
-                                        &ctx->mpls_depth_delta);
+                                        &ctx->xout->wc);
 
   compose_flow_sample_cookie(os->probability, os->collector_set_id,
                              os->obs_domain_id, os->obs_point_id, &cookie);
@@ -2770,38 +2675,24 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_PUSH_MPLS:
-            if (compose_mpls_push_action(ctx,
-                                         ofpact_get_PUSH_MPLS(a)->ethertype)) {
-                return;
-            }
+            compose_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a));
             break;
 
         case OFPACT_POP_MPLS:
-            if (compose_mpls_pop_action(ctx,
-                                        ofpact_get_POP_MPLS(a)->ethertype)) {
-                return;
-            }
+            compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
             break;
 
         case OFPACT_SET_MPLS_LABEL:
-            if (compose_set_mpls_label_action(ctx,
-                                              ofpact_get_SET_MPLS_LABEL(a)->label)) {
-                return;
-            }
-            break;
+            compose_set_mpls_label_action(
+                ctx, ofpact_get_SET_MPLS_LABEL(a)->label);
+        break;
 
         case OFPACT_SET_MPLS_TC:
-            if (compose_set_mpls_tc_action(ctx,
-                                           ofpact_get_SET_MPLS_TC(a)->tc)) {
-                return;
-            }
+            compose_set_mpls_tc_action(ctx, ofpact_get_SET_MPLS_TC(a)->tc);
             break;
 
         case OFPACT_SET_MPLS_TTL:
-            if (compose_set_mpls_ttl_action(ctx,
-                                            ofpact_get_SET_MPLS_TTL(a)->ttl)) {
-                return;
-            }
+            compose_set_mpls_ttl_action(ctx, ofpact_get_SET_MPLS_TTL(a)->ttl);
             break;
 
         case OFPACT_DEC_MPLS_TTL:
@@ -3092,7 +2983,6 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
     ctx.orig_skb_priority = flow->skb_priority;
     ctx.table_id = 0;
     ctx.exit = false;
-    ctx.mpls_depth_delta = 0;
 
     if (!xin->ofpacts && !ctx.rule) {
         rule_dpif_lookup(ctx.xbridge->ofproto, flow,
index 982f1a4..8b01d4e 100644 (file)
@@ -129,7 +129,8 @@ void xlate_ofproto_set(struct ofproto_dpif *, const char *name,
                        const struct mbridge *, const struct dpif_sflow *,
                        const struct dpif_ipfix *, const struct netflow *,
                        enum ofp_config_flags, bool forward_bpdu,
-                       bool has_in_band, bool variable_length_userdata)
+                       bool has_in_band, bool variable_length_userdata,
+                       size_t mpls_label_stack_length)
     OVS_REQ_WRLOCK(xlate_rwlock);
 void xlate_remove_ofproto(struct ofproto_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
 
@@ -152,8 +153,7 @@ void xlate_ofport_remove(struct ofport_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
 
 int xlate_receive(const struct dpif_backer *, struct ofpbuf *packet,
                   const struct nlattr *key, size_t key_len,
-                  struct flow *, enum odp_key_fitness *,
-                  struct ofproto_dpif **, struct dpif_ipfix **,
+                  struct flow *, struct ofproto_dpif **, struct dpif_ipfix **,
                   struct dpif_sflow **, struct netflow **,
                   odp_port_t *odp_in_port)
     OVS_EXCLUDED(xlate_rwlock);
index cc1e9d5..7b3e1eb 100644 (file)
@@ -256,6 +256,10 @@ struct dpif_backer {
      * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions.
      * False if the datapath supports only 8-byte (or shorter) userdata. */
     bool variable_length_userdata;
+
+    /* Maximum number of MPLS label stack entries that the datapath supports
+     * in a match */
+    size_t max_mpls_depth;
 };
 
 /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
@@ -319,6 +323,12 @@ ofproto_dpif_cast(const struct ofproto *ofproto)
     return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
 }
 
+size_t
+ofproto_dpif_get_max_mpls_depth(const struct ofproto_dpif *ofproto)
+{
+    return ofproto->backer->max_mpls_depth;
+}
+
 static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *ofproto,
                                         ofp_port_t ofp_port);
 static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
@@ -558,7 +568,8 @@ type_run(const char *type)
                               ofproto->netflow, ofproto->up.frag_handling,
                               ofproto->up.forward_bpdu,
                               connmgr_has_in_band(ofproto->up.connmgr),
-                              ofproto->backer->variable_length_userdata);
+                              ofproto->backer->variable_length_userdata,
+                              ofproto->backer->max_mpls_depth);
 
             HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
                 xlate_bundle_set(ofproto, bundle, bundle->name,
@@ -781,6 +792,7 @@ struct odp_garbage {
 };
 
 static bool check_variable_length_userdata(struct dpif_backer *backer);
+static size_t check_max_mpls_depth(struct dpif_backer *backer);
 
 static int
 open_dpif_backer(const char *type, struct dpif_backer **backerp)
@@ -881,6 +893,7 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp)
         return error;
     }
     backer->variable_length_userdata = check_variable_length_userdata(backer);
+    backer->max_mpls_depth = check_max_mpls_depth(backer);
 
     if (backer->recv_set_enable) {
         udpif_set_threads(backer->udpif, n_handlers, n_revalidators);
@@ -965,6 +978,53 @@ check_variable_length_userdata(struct dpif_backer *backer)
     }
 }
 
+/* Tests the MPLS label stack depth supported by 'backer''s datapath.
+ *
+ * Returns the number of elements in a struct flow's mpls_lse field
+ * if the datapath supports at least that many entries in an
+ * MPLS label stack.
+ * Otherwise returns the number of MPLS push actions supported by
+ * the datapath. */
+static size_t
+check_max_mpls_depth(struct dpif_backer *backer)
+{
+    struct flow flow;
+    int n;
+
+    for (n = 0; n < FLOW_MAX_MPLS_LABELS; n++) {
+        struct odputil_keybuf keybuf;
+        struct ofpbuf key;
+        int error;
+
+        memset(&flow, 0, sizeof flow);
+        flow.dl_type = htons(ETH_TYPE_MPLS);
+        flow_set_mpls_bos(&flow, n, 1);
+
+        ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+        odp_flow_key_from_flow(&key, &flow, 0);
+
+        error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+                              key.data, key.size, NULL, 0, NULL, 0, NULL);
+        if (error && error != EEXIST) {
+            if (error != EINVAL) {
+                VLOG_WARN("%s: MPLS stack length feature probe failed (%s)",
+                          dpif_name(backer->dpif), ovs_strerror(error));
+            }
+            break;
+        }
+
+        error = dpif_flow_del(backer->dpif, key.data, key.size, NULL);
+        if (error) {
+            VLOG_WARN("%s: failed to delete MPLS feature probe flow",
+                      dpif_name(backer->dpif));
+        }
+    }
+
+    VLOG_INFO("%s: MPLS label stack length probed as %d",
+              dpif_name(backer->dpif), n);
+    return n;
+}
+
 static int
 construct(struct ofproto *ofproto_)
 {
@@ -3655,7 +3715,7 @@ parse_flow_and_packet(int argc, const char *argv[],
         }
 
         if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, flow,
-                          NULL, ofprotop, NULL, NULL, NULL, NULL)) {
+                          ofprotop, NULL, NULL, NULL, NULL)) {
             error = "Invalid datapath flow";
             goto exit;
         }
@@ -4040,11 +4100,10 @@ static bool
 ofproto_dpif_contains_flow(const struct ofproto_dpif *ofproto,
                            const struct nlattr *key, size_t key_len)
 {
-    enum odp_key_fitness fitness;
     struct ofproto_dpif *ofp;
     struct flow flow;
 
-    xlate_receive(ofproto->backer, NULL, key, key_len, &flow, &fitness, &ofp,
+    xlate_receive(ofproto->backer, NULL, key, key_len, &flow, &ofp,
                   NULL, NULL, NULL, NULL);
     return ofp == ofproto;
 }
index 5da0b5d..d09e285 100644 (file)
@@ -65,6 +65,8 @@ extern struct ovs_rwlock xlate_rwlock;
  *   Ofproto-dpif-xlate is responsible for translating translating OpenFlow
  *   actions into datapath actions. */
 
+size_t ofproto_dpif_get_max_mpls_depth(const struct ofproto_dpif *);
+
 void rule_dpif_lookup(struct ofproto_dpif *, const struct flow *,
                       struct flow_wildcards *, struct rule_dpif **rule);
 
index b2cdb10..f2a88bb 100644 (file)
@@ -734,12 +734,12 @@ ofproto_set_mac_table_config(struct ofproto *ofproto, unsigned idle_time,
 }
 
 void
-ofproto_set_threads(size_t n_handlers_, size_t n_revalidators_)
+ofproto_set_threads(int n_handlers_, int n_revalidators_)
 {
     int threads = MAX(count_cpu_cores(), 2);
 
-    n_revalidators = n_revalidators_;
-    n_handlers = n_handlers_;
+    n_revalidators = MAX(n_revalidators_, 0);
+    n_handlers = MAX(n_handlers_, 0);
 
     if (!n_revalidators) {
         n_revalidators = n_handlers
index 3034d32..0ac4454 100644 (file)
@@ -247,7 +247,7 @@ void ofproto_set_flow_miss_model(unsigned model);
 void ofproto_set_forward_bpdu(struct ofproto *, bool forward_bpdu);
 void ofproto_set_mac_table_config(struct ofproto *, unsigned idle_time,
                                   size_t max_entries);
-void ofproto_set_threads(size_t n_handlers, size_t n_revalidators);
+void ofproto_set_threads(int n_handlers, int n_revalidators);
 void ofproto_set_dp_desc(struct ofproto *, const char *dp_desc);
 int ofproto_set_snoops(struct ofproto *, const struct sset *snoops);
 int ofproto_set_netflow(struct ofproto *,
index 269d4f4..6924d42 100644 (file)
@@ -17,6 +17,7 @@ ovsdb\-server \- Open vSwitch database server
 [\fB\-\-remote=\fIremote\fR]\&...
 [\fB\-\-run=\fIcommand\fR]
 .so lib/daemon-syn.man
+.so lib/service-syn.man
 .so lib/vlog-syn.man
 .so lib/ssl-syn.man
 .so lib/ssl-bootstrap-syn.man
@@ -92,6 +93,8 @@ run a single command, e.g.:
 \fBovsdb\-server\fR detaches only after it starts listening on all \
 configured remotes.
 .so lib/daemon.man
+.SS "Service Options"
+.so lib/service.man
 .SS "Logging Options"
 .so lib/vlog.man
 .SS "Public Key Infrastructure Options"
index 4105a95..c24d355 100644 (file)
@@ -136,6 +136,7 @@ main(int argc, char *argv[])
 
     proctitle_init(argc, argv);
     set_program_name(argv[0]);
+    service_start(&argc, &argv);
     signal(SIGPIPE, SIG_IGN);
     process_init();
 
@@ -302,6 +303,9 @@ main(int argc, char *argv[])
         }
         poll_timer_wait_until(status_timer);
         poll_block();
+        if (should_service_stop()) {
+            exiting = true;
+        }
     }
     ovsdb_jsonrpc_server_destroy(jsonrpc);
     SHASH_FOR_EACH(node, &all_dbs) {
@@ -319,6 +323,7 @@ main(int argc, char *argv[])
         }
     }
 
+    service_stop();
     return 0;
 }
 
index f74d7f0..4a704c3 100644 (file)
@@ -78,23 +78,12 @@ def set_pidfile(name):
     _pidfile = make_pidfile_name(name)
 
 
-def get_pidfile():
-    """Returns an absolute path to the configured pidfile, or None if no
-    pidfile is configured."""
-    return _pidfile
-
-
 def set_no_chdir():
     """Sets that we do not chdir to "/"."""
     global _chdir
     _chdir = False
 
 
-def is_chdir_enabled():
-    """Will we chdir to "/" as part of daemonizing?"""
-    return _chdir
-
-
 def ignore_existing_pidfile():
     """Normally, daemonize() or daemonize_start() will terminate the program
     with a message if a locked pidfile already exists.  If this function is
index 7c30e10..9cb4d83 100644 (file)
@@ -6,7 +6,7 @@ AT_CAPTURE_FILE([pid])
 AT_CAPTURE_FILE([expected])
 # Start the daemon and wait for the pidfile to get created
 # and that its contents are the correct pid.
-AT_CHECK([ovsdb-server --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db& echo $! > expected], [0])
+AT_CHECK([ovsdb-server --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db 2>/dev/null & echo $! > expected], [0])
 OVS_WAIT_UNTIL([test -s pid], [kill `cat expected`])
 AT_CHECK(
   [pid=`cat pid` && expected=`cat expected` && test "$pid" = "$expected"],
@@ -25,7 +25,7 @@ AT_CAPTURE_FILE([parent])
 AT_CAPTURE_FILE([parentpid])
 AT_CAPTURE_FILE([newpid])
 # Start the daemon and wait for the pidfile to get created.
-AT_CHECK([ovsdb-server --monitor --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db& echo $! > parent], [0])
+AT_CHECK([ovsdb-server --monitor --pidfile="`pwd`"/pid --remote=punix:socket --unixctl="`pwd`"/unixctl db 2>/dev/null & echo $! > parent], [0])
 OVS_WAIT_UNTIL([test -s pid], [kill `cat parent`])
 # Check that the pidfile names a running process,
 # and that the parent process of that process is our child process.
index b505345..a968dd3 100644 (file)
@@ -79,9 +79,19 @@ sed 's/^/skb_priority(0),skb_mark(0),/' odp-base.txt | sed -n 's/,frag=no),/,fra
  echo
  echo '# Valid forms with IP later fragment.'
 sed 's/^/skb_priority(0),skb_mark(0),/' odp-base.txt | sed -n 's/,frag=no),.*/,frag=later)/p'
-) > odp.txt
-AT_CAPTURE_FILE([odp.txt])
-AT_CHECK_UNQUOTED([test-odp parse-keys < odp.txt], [0], [`cat odp.txt`
+) > odp-in.txt
+AT_CAPTURE_FILE([odp-in.txt])
+
+dnl If the BoS bit of the last LSE is 0 then the stack is unterminated
+dnl Internally a stack of 3 LSEs will be used with the trailing LSEs
+dnl set to zero. This is reflected when the key is formated
+sed '/bos=0/{
+s/^/ODP_FIT_TOO_LITTLE: /
+s/mpls(label=100,tc=7,ttl=100,bos=0)/mpls(lse0=0x64e64,lse1=0,lse2=0)/
+s/mpls(label=1000,tc=4,ttl=200,bos=0)/mpls(lse0=0x3e88c8,lse1=0,lse2=0)/
+}' < odp-in.txt > odp-out.txt
+
+AT_CHECK_UNQUOTED([test-odp parse-keys < odp-in.txt], [0], [`cat odp-out.txt`
 ])
 AT_CLEANUP
 
index 730bb91..1c1f029 100644 (file)
@@ -1,5 +1,51 @@
 AT_BANNER([ofproto-dpif])
 
+# Strips out uninteresting parts of flow output, as well as parts
+# that vary from one run to another (e.g., timing and bond actions).
+m4_define([STRIP_USED], [[sed '
+    s/used:[0-9]*\.[0-9]*/used:0.0/
+' | sort]])
+m4_define([STRIP_XOUT], [[sed '
+    s/used:[0-9]*\.[0-9]*/used:0.0/
+    s/actions:.*/actions: <del>/
+    s/packets:[0-9]*/packets:0/
+    s/bytes:[0-9]*/bytes:0/
+' | sort]])
+
+AT_SETUP([ofproto-dpif - dummy interface])
+# Create br0 with interfaces p1 and p7
+#    and br1 with interfaces p2 and p8
+# with p1 and p2 connected via unix domain socket
+OVS_VSWITCHD_START(
+  [add-port br0 p1 -- set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p0.sock ofport_request=1 -- \
+   add-port br0 p7 -- set interface p7 ofport_request=7 type=dummy -- \
+   add-br br1 -- \
+   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
+   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
+                  fail-mode=secure -- \
+   add-port br1 p2 -- set interface p2 type=dummy options:stream=unix:$OVS_RUNDIR/p0.sock ofport_request=2 -- \
+   add-port br1 p8 -- set interface p8 ofport_request=8 type=dummy --])
+
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+AT_CHECK([ovs-ofctl add-flow br1 action=normal])
+ovs-appctl time/stop
+ovs-appctl time/warp 5000
+AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+ovs-appctl time/warp 100
+
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3/0.0.0.0,dst=10.0.0.4/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(2),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+skb_priority(0),in_port(8),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.3/0.0.0.0,dst=10.0.0.4/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - resubmit])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [10], [11], [12], [13], [14], [15],
@@ -549,13 +595,13 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=46912
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=46912
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=46912
 ])
 
 dnl Modified MPLS controller action.
@@ -809,6 +855,7 @@ for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=80:88:88:88:88:88,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
 done
 
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 9])
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
@@ -840,6 +887,7 @@ for i in 1 ; do
     ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 20 22 22 22 22 22 08 00 45 00 00 24 00 00 00 00 00 84 00 00 C0 A8 00 01 C0 A8 00 02 04 58 08 af 00 00 00 00 d9 d7 91 57 01 00 00 34 cf 28 ec 4e 00 01 40 00 00 0a ff ff b7 53 24 19 00 05 00 08 7f 00 00 01 00 05 00 08 c0 a8 02 07 00 0c 00 06 00 05 00 00 80 00 00 04 c0 00 00 04'
 done
 
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 9])
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=98 in_port=1 (via action) data_len=98 (unbuffered)
@@ -1809,9 +1857,15 @@ dnl it deterministic. Ensuring that we send at least two packets
 dnl into each port means we get to check the seq nos are
 dnl incrementing correctly.
 
+dnl because packets from different ports can be handled by separate
+dnl threads, put some sleeps
+
 ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
+sleep 1
 ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:07,tha=00:00:00:00:00:00)'
+sleep 1
 ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+sleep 1
 ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'
 ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x86dd),ipv6(src=fe80::1,dst=fe80::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'
 
@@ -2380,6 +2434,76 @@ skb_priority(0),skb_mark(0/0),in_port(p3),eth(src=50:54:00:00:00:09/00:00:00:00:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-flows - MPLS actions that result in a userspace action])
+OVS_VSWITCHD_START([dnl
+   add-port br0 p1 -- set Interface p1 type=dummy
+])
+ON_EXIT([kill `cat ovs-ofctl.pid`])
+
+AT_CAPTURE_FILE([ofctl_monitor.log])
+AT_DATA([flows.txt], [dnl
+dl_src=60:66:66:66:66:00 actions=push_mpls:0x8847,controller
+dl_src=60:66:66:66:66:01 actions=pop_mpls:0x8849,controller
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+dnl Packet is sent to userspace because a MPLS push or pop action is applied to
+dnl a packet with 4 MPLS LSEs but userspace and the datapath can only handle up
+dnl to 3 labels.
+dnl
+dnl The input is a frame with four MPLS labels which tcpdump -vve shows as:
+dnl 60:66:66:66:66:00 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 74: MPLS (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, [S], ttl 32)
+dnl         (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto TCP (6), length 44, bad cksum 3b78 (->f978)!)
+dnl     192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4
+
+for dl_src in 00 01; do
+    AT_CHECK([ovs-appctl netdev-dummy/receive p1 "505400000007 6066666666$dl_src 8847 00014020 00014020 00014020 00014120 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45"])
+
+    AT_CHECK_UNQUOTED([ovs-appctl dpif/dump-flows br0 | grep ":$dl_src/" | STRIP_USED], [0], [dnl
+skb_priority(0),in_port(1),eth(src=60:66:66:66:66:$dl_src/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x8847),mpls(lse0=0x14020/0x100,lse1=0x14020/0x100,lse2=0x14020/0x100), packets:0, bytes:0, used:never, actions:drop
+])
+done
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-flows - MPLS actions that result in a drop])
+OVS_VSWITCHD_START([dnl
+   add-port br0 p1 -- set Interface p1 type=dummy
+])
+ON_EXIT([kill `cat ovs-ofctl.pid`])
+
+AT_CAPTURE_FILE([ofctl_monitor.log])
+AT_DATA([flows.txt], [dnl
+dl_src=60:66:66:66:66:00 actions=push_mpls:0x8847,controller
+dl_src=60:66:66:66:66:01 actions=pop_mpls:0x8849,controller
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+dnl Packet is dropped because an MPLS PUSH action is applied to a packet with
+dnl 4 MPLS LSEs but ovs-vswtichd can only handle up to 3 MPLS LSEs and thus
+dnl can't determine the resulting MPLS label after an MPLS PUSH action.
+dnl
+dnl The input is a frame with two MPLS headers which tcpdump -vve shows as:
+dnl 60:66:66:66:66:01 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 74: MPLS (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, [S], ttl 32)
+dnl         (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto TCP (6), length 44, bad cksum 3b78 (->f978)!)
+dnl     192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 66 01 88 47 00 01 40 20 00 01 40 20 00 01 40 20 00 01 41 20 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45'])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
+skb_priority(0),in_port(1),eth(src=60:66:66:66:66:01/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x8847),mpls(lse0=0x14020/0x100,lse1=0x14020/0x100,lse2=0x14020/0x100), packets:0, bytes:0, used:never, actions:drop
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - patch ports])
 OVS_VSWITCHD_START([add-br br1 \
 -- set bridge br1 datapath-type=dummy fail-mode=secure \
@@ -2396,20 +2520,14 @@ AT_CHECK([ovs-ofctl add-flow br1 actions=LOCAL,output:1,output:3])
 
 for i in $(seq 1 10); do
     ovs-appctl netdev-dummy/receive br0 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
-    if [[ $i -eq 1 ]]; then
-        sleep 1
-    fi
 done
 
 for i in $(seq 1 5); do
     ovs-appctl netdev-dummy/receive br1 'in_port(101),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
-    if [[ $i -eq 1 ]]; then
-        sleep 1
-    fi
 done
 
-AT_CHECK([ovs-appctl time/warp 1000 && ovs-appctl time/warp 1000], [0], [warped
-warped
+AT_CHECK([ovs-appctl time/warp 500], [0],
+[warped
 ])
 
 AT_CHECK([ovs-appctl dpif/show], [0], [dnl
@@ -2466,18 +2584,6 @@ AT_CLEANUP
 dnl ----------------------------------------------------------------------
 AT_BANNER([ofproto-dpif -- megaflows])
 
-# Strips out uninteresting parts of megaflow output, as well as parts
-# that vary from one run to another (e.g., timing and bond actions).
-m4_define([STRIP_USED], [[sed '
-    s/used:[0-9]*\.[0-9]*/used:0.0/
-' | sort]])
-m4_define([STRIP_XOUT], [[sed '
-    s/used:[0-9]*\.[0-9]*/used:0.0/
-    s/actions:.*/actions: <del>/
-    s/packets:[0-9]*/packets:0/
-    s/bytes:[0-9]*/bytes:0/
-' | sort]])
-
 AT_SETUP([ofproto-dpif megaflow - port classification])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [2])
@@ -2487,6 +2593,7 @@ table=0 in_port=1 actions=output(2)
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
 ])
@@ -2502,6 +2609,7 @@ table=0 in_port=1,dl_src=50:54:00:00:00:09 actions=output(2)
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2519,6 +2627,7 @@ table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=output(2)
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.252,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2536,6 +2645,7 @@ table=0 in_port=1,ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=output(2)
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:1:2:3:4:5,dst=fe80::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:5:4:3:2:1,dst=2001:db8:3c4d:1:2:3:4:1,label=0,proto=99,tclass=0x70,hlimit=64,frag=no)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:1:2:3:4:5/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,dst=fe80::2/::,label=0/0,proto=10/0,tclass=0x70/0,hlimit=128/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth_type(0x86dd),ipv6(src=2001:db8:3c4d:5:4:3:2:1/ffff:ffff:ffff:fffc::,dst=2001:db8:3c4d:1:2:3:4:1/::,label=0/0,proto=99/0,tclass=0x70/0,hlimit=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2564,6 +2674,7 @@ ADD_OF_PORTS([br0], [1], [2])
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2581,6 +2692,7 @@ table=0 dl_src=50:54:00:00:00:0b actions=pop_mpls:0x0800,2
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1), packets:0, bytes:0, used:never, actions: <del>
@@ -2605,6 +2717,7 @@ ovs-vsctl \
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
@@ -2624,6 +2737,7 @@ AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2643,6 +2757,7 @@ AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2679,7 +2794,7 @@ ovs-appctl time/stop
 ovs-appctl time/warp 5000
 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(7),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:never, actions: <del>
@@ -2697,6 +2812,7 @@ table=0 in_port=90,dl_src=50:54:00:00:00:09 actions=output(2)
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2715,6 +2831,7 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=
 1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2732,6 +2849,7 @@ table=1 dl_src=50:54:00:00:00:09 actions=output(2)
 AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2753,6 +2871,7 @@ in_port=1 actions=output:2
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
 ])
@@ -2773,6 +2892,7 @@ in_port=1 actions=output:2
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8100),vlan(vid=11,pcp=7),encap(eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0))'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth_type(0x8100),vlan(vid=11/0xfff,pcp=7/0x0,cfi=1/1),encap(eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff)), packets:0, bytes:0, used:never, actions: <del>
@@ -2791,6 +2911,7 @@ table=0 in_port=91 reg0=0x0a000002,actions=output(2)
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2807,6 +2928,7 @@ table=0 in_port=1 ip,actions=push:NXM_OF_IP_SRC[[]],output(2)
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2831,6 +2953,7 @@ for i in 1 2; do
     AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
     ovs-appctl time/warp 100
 done
+sleep 1
 dnl The original flow is missing due to a revalidation.
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
@@ -2860,6 +2983,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0x1,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0xfd,ttl=128,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0x1,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0xfd/0x3,ttl=128/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
 skb_priority(0),in_port(3),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0xfd/0xff,ttl=128/0xff,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
@@ -2878,6 +3002,7 @@ table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=dec_ttl,output(2)
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.252,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:never, actions: <del>
 skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no), packets:0, bytes:0, used:never, actions: <del>
@@ -2894,6 +3019,7 @@ table=0 in_port=1 actions=mod_dl_dst(50:54:00:00:00:0a),output(2)
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
+sleep 1
 dnl The megaflows do not match the same fields, since the first packet
 dnl is essentially a no-op.  (The new destination MAC is the same as the
 dnl original.) The ofproto-dpif library un-wildcards the destination MAC
@@ -2926,6 +3052,7 @@ for i in 1 2 3 4; do
     AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
     AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
 done
+sleep 1
 AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_USED], [0], [dnl
 skb_priority(0),skb_mark(0),in_port(1/0xffff),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0xff,ttl=64/0xff,frag=no/0xfc),icmp(type=8,code=0), packets:3, bytes:180, used:0.0s, actions:2
 skb_priority(0),skb_mark(0),in_port(1/0xffff),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0xff,ttl=64/0xff,frag=no/0xfc),icmp(type=8,code=0), packets:3, bytes:180, used:0.0s, actions:drop
index 99a9e69..2910035 100644 (file)
@@ -48,7 +48,7 @@ main(int argc OVS_UNUSED, char *argv[])
         ovs_fatal(errno, "failed to open fd 3 for reading");
     }
 
-    retval = pcap_read_header(pcap);
+    retval = ovs_pcap_read_header(pcap);
     if (retval) {
         ovs_fatal(retval > 0 ? retval : 0, "reading pcap header failed");
     }
@@ -61,7 +61,7 @@ main(int argc OVS_UNUSED, char *argv[])
         union flow_in_port in_port_;
         n++;
 
-        retval = pcap_read(pcap, &packet, NULL);
+        retval = ovs_pcap_read(pcap, &packet, NULL);
         if (retval == EOF) {
             ovs_fatal(0, "unexpected end of file reading pcap file");
         } else if (retval) {
index e381b2b..cfb74eb 100644 (file)
@@ -119,6 +119,10 @@ module.
 \fBsyslog\fR, \fBconsole\fR, or \fBfile\fR, to limit the log level
 change to only to the system log, to the console, or to a file,
 respectively.
+.IP
+On Windows platform, \fBsyslog\fR is accepted as a word and
+is only useful if the \fItarget\fR was started with the
+\fB\-\-syslog\-target\fR option (the word has no effect otherwise).
 .
 .IP \(bu 
 \fBoff\fR, \fBemer\fR, \fBerr\fR, \fBwarn\fR, \fBinfo\fR, or
index 7b69094..3f32e58 100755 (executable)
@@ -28,7 +28,7 @@ BUILD_GCC = OVS_SRC + "/_build-gcc"
 BUILD_CLANG = OVS_SRC + "/_build-clang"
 PATH = "%(ovs)s/utilities:%(ovs)s/ovsdb:%(ovs)s/vswitchd" % {"ovs": BUILD_GCC}
 
-ENV["CFLAGS"] = "-g -O0"
+ENV["CFLAGS"] = "-g -fno-omit-frame-pointer"
 ENV["PATH"] = PATH + ":" + ENV["PATH"]
 
 options = None
@@ -62,8 +62,7 @@ def conf():
 
     configure = ["../configure", "--prefix=" + ROOT, "--localstatedir=" + ROOT,
                  "--with-logdir=%s/log" % ROOT, "--with-rundir=%s/run" % ROOT,
-                 "--with-linux=/lib/modules/%s/build" % uname(),
-                 "--with-dbdir=" + ROOT]
+                 "--enable-silent-rules", "--with-dbdir=" + ROOT, "--silent"]
 
     if options.werror:
         configure.append("--enable-Werror")
@@ -74,6 +73,10 @@ def conf():
     if options.mandir:
         configure.append("--mandir=" + options.mandir)
 
+    if options.optimize is None:
+        options.optimize = 0
+    ENV["CFLAGS"] = "%s -O%d" % (ENV["CFLAGS"], options.optimize)
+
     _sh("./boot.sh")
 
     try:
@@ -82,7 +85,7 @@ def conf():
         pass # Directory exists.
 
     os.chdir(BUILD_GCC)
-    _sh(*configure)
+    _sh(*(configure + ["--with-linux=/lib/modules/%s/build" % uname()]))
 
     try:
         _sh("clang --version", check=True)
@@ -320,6 +323,10 @@ def main():
                      action="store_true", help="configure with cached timing")
     group.add_option("--mandir", dest="mandir", metavar="MANDIR",
                      help="configure the man documentation install directory")
+
+    for i in range(4):
+        group.add_option("--O%d" % i, dest="optimize", action="store_const",
+                         const=i, help="compile with -O%d" % i)
     parser.add_option_group(group)
 
     group = optparse.OptionGroup(parser, "run")
index 09db084..3b1dff1 100644 (file)
@@ -1192,9 +1192,9 @@ dpctl_normalize_actions(int argc, char *argv[])
 
         if (eth_type_mpls(af->flow.dl_type)) {
             printf("mpls(label=%"PRIu32",tc=%d,ttl=%d): ",
-                   mpls_lse_to_label(af->flow.mpls_lse),
-                   mpls_lse_to_tc(af->flow.mpls_lse),
-                   mpls_lse_to_ttl(af->flow.mpls_lse));
+                   mpls_lse_to_label(af->flow.mpls_lse[0]),
+                   mpls_lse_to_tc(af->flow.mpls_lse[0]),
+                   mpls_lse_to_ttl(af->flow.mpls_lse[0]));
         } else {
             printf("no mpls: ");
         }
index e8453f3..69dd34f 100644 (file)
@@ -1852,7 +1852,7 @@ ofctl_ofp_parse_pcap(int argc OVS_UNUSED, char *argv[])
     int error;
     bool first;
 
-    file = pcap_open(argv[1], "rb");
+    file = ovs_pcap_open(argv[1], "rb");
     if (!file) {
         ovs_fatal(errno, "%s: open failed", argv[1]);
     }
@@ -1864,7 +1864,7 @@ ofctl_ofp_parse_pcap(int argc OVS_UNUSED, char *argv[])
         long long int when;
         struct flow flow;
 
-        error = pcap_read(file, &packet, &when);
+        error = ovs_pcap_read(file, &packet, &when);
         if (error) {
             break;
         }
@@ -3199,7 +3199,7 @@ ofctl_parse_pcap(int argc OVS_UNUSED, char *argv[])
 {
     FILE *pcap;
 
-    pcap = pcap_open(argv[1], "rb");
+    pcap = ovs_pcap_open(argv[1], "rb");
     if (!pcap) {
         ovs_fatal(errno, "%s: open failed", argv[1]);
     }
@@ -3209,7 +3209,7 @@ ofctl_parse_pcap(int argc OVS_UNUSED, char *argv[])
         struct flow flow;
         int error;
 
-        error = pcap_read(pcap, &packet, NULL);
+        error = ovs_pcap_read(pcap, &packet, NULL);
         if (error == EOF) {
             break;
         } else if (error) {
index 528b40c..af6ae5f 100644 (file)
@@ -3352,6 +3352,7 @@ set_column(const struct vsctl_table_class *table,
 
         ovsdb_datum_union(&datum, ovsdb_idl_read(row, column),
                           &column->type, false);
+        ovsdb_idl_txn_verify(row, column);
         ovsdb_idl_txn_write(row, column, &datum);
     } else {
         struct ovsdb_datum datum;
index e6be975..5b1aec3 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -575,6 +575,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
             port_configure(port);
 
             LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+                iface_set_ofport(iface->cfg, iface->ofp_port);
                 iface_configure_cfm(iface);
                 iface_configure_qos(iface, port->cfg->qos);
                 iface_set_mac(iface, port->cfg->fake_bridge ? br->ea : NULL);
@@ -1478,7 +1479,6 @@ iface_create(struct bridge *br, const struct ovsrec_interface *iface_cfg,
     ovs_assert(!iface_lookup(br, iface_cfg->name));
     error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev);
     if (error) {
-        iface_set_ofport(iface_cfg, OFPP_NONE);
         iface_clear_db_record(iface_cfg);
         return false;
     }
@@ -1503,8 +1503,6 @@ iface_create(struct bridge *br, const struct ovsrec_interface *iface_cfg,
     hmap_insert(&br->ifaces, &iface->ofp_port_node,
                 hash_ofp_port(ofp_port));
 
-    iface_set_ofport(iface->cfg, ofp_port);
-
     /* Populate initial status in database. */
     iface_refresh_stats(iface);
     iface_refresh_status(iface);
@@ -3539,6 +3537,7 @@ static void
 iface_clear_db_record(const struct ovsrec_interface *if_cfg)
 {
     if (!ovsdb_idl_row_is_synthetic(&if_cfg->header_)) {
+        iface_set_ofport(if_cfg, OFPP_NONE);
         ovsrec_interface_set_status(if_cfg, NULL);
         ovsrec_interface_set_admin_state(if_cfg, NULL);
         ovsrec_interface_set_duplex(if_cfg, NULL);
index 0dd091f..d2544f7 100644 (file)
@@ -86,11 +86,14 @@ only allow privileged users, such as the superuser, to use it.
 \fBovs\-vswitchd\fR emits a log message if \fBmlockall()\fR is
 unavailable or unsuccessful.
 .
+.SS "Daemon Options"
 .ds DD \
 \fBovs\-vswitchd\fR detaches only after it has connected to the \
 database, retrieved the initial configuration, and set up that \
 configuration.
 .so lib/daemon.man
+.SS "Service Options"
+.so lib/service.man
 .SS "Public Key Infrastructure Options"
 .so lib/ssl.man
 .so lib/ssl-bootstrap.man
index 990e58f..9da2f49 100644 (file)
@@ -73,6 +73,7 @@ main(int argc, char *argv[])
 
     proctitle_init(argc, argv);
     set_program_name(argv[0]);
+    service_start(&argc, &argv);
     remote = parse_options(argc, argv, &unixctl_path);
     signal(SIGPIPE, SIG_IGN);
     sighup = signal_register(SIGHUP);
@@ -127,9 +128,13 @@ main(int argc, char *argv[])
             poll_immediate_wake();
         }
         poll_block();
+        if (should_service_stop()) {
+            exiting = true;
+        }
     }
     bridge_exit();
     unixctl_server_destroy(unixctl);
+    service_stop();
 
     return 0;
 }
index 5fd82fc..797f330 100644 (file)
          expected as destination for received BFD packets.  The default is
          <code>00:23:20:00:00:01</code>.
        </column>
+
+       <column name="bfd" key="bfd_src_ip">
+          Set to an IPv4 address to set the IP address used as source for
+          transmitted BFD packets.  The default is <code>169.254.1.0</code>.
+       </column>
+
+       <column name="bfd" key="bfd_dst_ip">
+          Set to an IPv4 address to set the IP address used as destination
+          for transmitted BFD packets.  The default is <code>169.254.1.1</code>.
+       </column>
       </group>
 
       <group title="BFD Status">