Blob Blame History Raw
diff --git a/.cirrus.yml b/.cirrus.yml
index a7ae793bc4..a4d2a5bbcd 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -2,8 +2,8 @@ freebsd_build_task:
 
   freebsd_instance:
     matrix:
-      image_family: freebsd-12-2-snap
-      image_family: freebsd-11-4-snap
+      image_family: freebsd-12-3-snap
+      image_family: freebsd-13-0-snap
     cpu: 4
     memory: 4G
 
diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst
index af524251ff..530d36e25a 100644
--- a/Documentation/faq/releases.rst
+++ b/Documentation/faq/releases.rst
@@ -208,8 +208,8 @@ Q: What DPDK version does each Open vSwitch release work with?
     2.12.x       18.11.9
     2.13.x       19.11.10
     2.14.x       19.11.10
-    2.15.x       20.11.1
-    2.16.x       20.11.1
+    2.15.x       20.11.4
+    2.16.x       20.11.4
     2.17.x       21.11.0
     ============ ========
 
diff --git a/Documentation/intro/install/general.rst b/Documentation/intro/install/general.rst
index c4300cd53e..a297aadac8 100644
--- a/Documentation/intro/install/general.rst
+++ b/Documentation/intro/install/general.rst
@@ -169,7 +169,7 @@ other than plain text, only if you have the following:
 If you are going to extensively modify Open vSwitch, consider installing the
 following to obtain better warnings:
 
-- "sparse" version 0.5.1 or later
+- "sparse" version 0.6.2 or later
   (https://git.kernel.org/pub/scm/devel/sparse/sparse.git/).
 
 - GNU make.
diff --git a/NEWS b/NEWS
index c10e9bfacc..8cae5f7de7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,21 @@
+v2.17.2 - xx xxx xxxx
+---------------------
+
+v2.17.1 - 08 Apr 2022
+---------------------
+   - Bug fixes
+   - libopenvswitch API change:
+     * To fix the Undefined Behavior issue causing the compiler to incorrectly
+       optimize important parts of code, container iteration macros (e.g.,
+       LIST_FOR_EACH) have been re-implemented in a UB-safe way.
+     * Backwards compatibility has mostly been preserved, however the
+       user-provided pointer is now set to NULL after the loop (unless it
+       exited via "break;")
+     * Users of libopenvswitch will need to double-check the use of such loop
+       macros before compiling with a new version.
+     * Since the change is limited to the definitions within the headers, the
+       ABI is not affected.
+
 v2.17.0 - 17 Feb 2022
 ---------------------
    - Userspace datapath:
diff --git a/acinclude.m4 b/acinclude.m4
index 0c360fd1ef..61e88105f5 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -305,6 +305,13 @@ AC_DEFUN([OVS_CHECK_LINUX_TC], [
     ])],
     [AC_DEFINE([HAVE_TCA_SKBEDIT_FLAGS], [1],
                [Define to 1 if TCA_SKBEDIT_FLAGS is available.])])
+
+  AC_COMPILE_IFELSE([
+    AC_LANG_PROGRAM([#include <linux/gen_stats.h>], [
+        int x = TCA_STATS_PKT64;
+    ])],
+    [AC_DEFINE([HAVE_TCA_STATS_PKT64], [1],
+               [Define to 1 if TCA_STATS_PKT64 is available.])])
 ])
 
 dnl OVS_CHECK_LINUX_SCTP_CT
@@ -1424,7 +1431,7 @@ AC_DEFUN([OVS_ENABLE_SPARSE],
    : ${SPARSE=sparse}
    AC_SUBST([SPARSE])
    AC_CONFIG_COMMANDS_PRE(
-     [CC='$(if $(C:0=),env REAL_CC="'"$CC"'" CHECK="$(SPARSE) $(SPARSE_WERROR) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')'])
+     [CC='$(if $(C:0=),env REAL_CC="'"$CC"'" CHECK="$(SPARSE) $(SPARSE_WERROR) -I $(top_srcdir)/include/sparse -I $(top_srcdir)/include $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')'])
 
    AC_ARG_ENABLE(
      [sparse],
diff --git a/configure.ac b/configure.ac
index 4e9bcce272..9ba141b223 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 AC_PREREQ(2.63)
-AC_INIT(openvswitch, 2.17.0, bugs@openvswitch.org)
+AC_INIT(openvswitch, 2.17.2, bugs@openvswitch.org)
 AC_CONFIG_SRCDIR([datapath/datapath.c])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c
index 70ac0a0e56..218e7db814 100644
--- a/datapath-windows/ovsext/Actions.c
+++ b/datapath-windows/ovsext/Actions.c
@@ -1712,6 +1712,15 @@ OvsUpdateIPv4Header(OvsForwardingContext *ovsFwdCtx,
         ipHdr->ttl = ipAttr->ipv4_ttl;
         key->ipKey.nwTtl = ipAttr->ipv4_ttl;
     }
+    if (ipHdr->dscp != (ipAttr->ipv4_tos & 0xfc)) {
+        /* ECN + DSCP */
+        UINT8 newTos = (ipHdr->tos & 0x3) | (ipAttr->ipv4_tos & 0xfc);
+        if (ipHdr->check != 0) {
+            ipHdr->check = ChecksumUpdate16(ipHdr->check, ipHdr->tos, newTos);
+        }
+        ipHdr->tos = newTos;
+        key->ipKey.nwTos = newTos;
+    }
 
     return NDIS_STATUS_SUCCESS;
 }
diff --git a/debian/changelog b/debian/changelog
index 3e0d3a66e3..c3b2852c28 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
+openvswitch (2.17.2-1) unstable; urgency=low
+   [ Open vSwitch team ]
+   * New upstream version
+
+ -- Open vSwitch team <dev@openvswitch.org>  Fri, 08 Apr 2022 14:57:49 +0200
+
+openvswitch (2.17.1-1) unstable; urgency=low
+   [ Open vSwitch team ]
+   * New upstream version
+
+ -- Open vSwitch team <dev@openvswitch.org>  Fri, 08 Apr 2022 14:57:49 +0200
+
 openvswitch (2.17.0-1) unstable; urgency=low
 
    * New upstream version
diff --git a/dpdk/lib/vhost/vhost_user.c b/dpdk/lib/vhost/vhost_user.c
index a781346c4d..550b0ee8b5 100644
--- a/dpdk/lib/vhost/vhost_user.c
+++ b/dpdk/lib/vhost/vhost_user.c
@@ -1603,6 +1603,9 @@ vhost_user_get_inflight_fd(struct virtio_net **pdev,
 	int numa_node = SOCKET_ID_ANY;
 	void *addr;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	if (msg->size != sizeof(msg->payload.inflight)) {
 		VHOST_LOG_CONFIG(ERR,
 			"invalid get_inflight_fd message size is %d\n",
@@ -1704,6 +1707,9 @@ vhost_user_set_inflight_fd(struct virtio_net **pdev, VhostUserMsg *msg,
 	int fd, i;
 	int numa_node = SOCKET_ID_ANY;
 
+	if (validate_msg_fds(msg, 1) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	fd = msg->fds[0];
 	if (msg->size != sizeof(msg->payload.inflight) || fd < 0) {
 		VHOST_LOG_CONFIG(ERR,
@@ -2873,6 +2879,9 @@ vhost_user_check_and_alloc_queue_pair(struct virtio_net *dev,
 	case VHOST_USER_SET_VRING_ADDR:
 		vring_idx = msg->payload.addr.index;
 		break;
+	case VHOST_USER_SET_INFLIGHT_FD:
+		vring_idx = msg->payload.inflight.num_queues - 1;
+		break;
 	default:
 		return 0;
 	}
diff --git a/include/linux/automake.mk b/include/linux/automake.mk
index 8f063f482e..f857c7e088 100644
--- a/include/linux/automake.mk
+++ b/include/linux/automake.mk
@@ -2,6 +2,7 @@ noinst_HEADERS += \
 	include/linux/netlink.h \
 	include/linux/netfilter/nf_conntrack_sctp.h \
 	include/linux/pkt_cls.h \
+	include/linux/gen_stats.h \
 	include/linux/tc_act/tc_mpls.h \
 	include/linux/tc_act/tc_pedit.h \
 	include/linux/tc_act/tc_skbedit.h \
diff --git a/include/linux/gen_stats.h b/include/linux/gen_stats.h
new file mode 100644
index 0000000000..6fae6f727c
--- /dev/null
+++ b/include/linux/gen_stats.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_GEN_STATS_WRAPPER_H
+#define __LINUX_GEN_STATS_WRAPPER_H 1
+
+#if defined(__KERNEL__) || defined(HAVE_TCA_STATS_PKT64)
+#include_next <linux/gen_stats.h>
+#else
+#include <linux/types.h>
+
+enum {
+	TCA_STATS_UNSPEC,
+	TCA_STATS_BASIC,
+	TCA_STATS_RATE_EST,
+	TCA_STATS_QUEUE,
+	TCA_STATS_APP,
+	TCA_STATS_RATE_EST64,
+	TCA_STATS_PAD,
+	TCA_STATS_BASIC_HW,
+	TCA_STATS_PKT64,
+	__TCA_STATS_MAX,
+};
+#define TCA_STATS_MAX (__TCA_STATS_MAX - 1)
+
+/**
+ * struct gnet_stats_basic - byte/packet throughput statistics
+ * @bytes: number of seen bytes
+ * @packets: number of seen packets
+ */
+struct gnet_stats_basic {
+	__u64	bytes;
+	__u32	packets;
+};
+
+/**
+ * struct gnet_stats_rate_est - rate estimator
+ * @bps: current byte rate
+ * @pps: current packet rate
+ */
+struct gnet_stats_rate_est {
+	__u32	bps;
+	__u32	pps;
+};
+
+/**
+ * struct gnet_stats_rate_est64 - rate estimator
+ * @bps: current byte rate
+ * @pps: current packet rate
+ */
+struct gnet_stats_rate_est64 {
+	__u64	bps;
+	__u64	pps;
+};
+
+/**
+ * struct gnet_stats_queue - queuing statistics
+ * @qlen: queue length
+ * @backlog: backlog size of queue
+ * @drops: number of dropped packets
+ * @requeues: number of requeues
+ * @overlimits: number of enqueues over the limit
+ */
+struct gnet_stats_queue {
+	__u32	qlen;
+	__u32	backlog;
+	__u32	drops;
+	__u32	requeues;
+	__u32	overlimits;
+};
+
+/**
+ * struct gnet_estimator - rate estimator configuration
+ * @interval: sampling period
+ * @ewma_log: the log of measurement window weight
+ */
+struct gnet_estimator {
+	signed char	interval;
+	unsigned char	ewma_log;
+};
+
+#endif /* __KERNEL__ || !HAVE_TCA_STATS_PKT64 */
+#endif /* __LINUX_GEN_STATS_WRAPPER_H */
diff --git a/include/openvswitch/hmap.h b/include/openvswitch/hmap.h
index 4e001cc692..beb48295b9 100644
--- a/include/openvswitch/hmap.h
+++ b/include/openvswitch/hmap.h
@@ -134,17 +134,17 @@ struct hmap_node *hmap_random_node(const struct hmap *);
  * without using 'break', NODE will be NULL.  This is true for all of the
  * HMAP_FOR_EACH_*() macros.
  */
-#define HMAP_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, HMAP)               \
-    for (INIT_CONTAINER(NODE, hmap_first_with_hash(HMAP, HASH), MEMBER); \
-         (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))                \
-         || ((NODE = NULL), false);                                     \
-         ASSIGN_CONTAINER(NODE, hmap_next_with_hash(&(NODE)->MEMBER),   \
-                          MEMBER))
-#define HMAP_FOR_EACH_IN_BUCKET(NODE, MEMBER, HASH, HMAP)               \
-    for (INIT_CONTAINER(NODE, hmap_first_in_bucket(HMAP, HASH), MEMBER); \
-         (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))                \
-         || ((NODE = NULL), false);                                     \
-         ASSIGN_CONTAINER(NODE, hmap_next_in_bucket(&(NODE)->MEMBER), MEMBER))
+#define HMAP_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, HMAP)                     \
+    for (INIT_MULTIVAR(NODE, MEMBER, hmap_first_with_hash(HMAP, HASH),        \
+                       struct hmap_node);                                     \
+         CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL);            \
+         UPDATE_MULTIVAR(NODE, hmap_next_with_hash(ITER_VAR(NODE))))
+
+#define HMAP_FOR_EACH_IN_BUCKET(NODE, MEMBER, HASH, HMAP)                     \
+    for (INIT_MULTIVAR(NODE, MEMBER, hmap_first_in_bucket(HMAP, HASH),        \
+                       struct hmap_node);                                     \
+         CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL);            \
+         UPDATE_MULTIVAR(NODE, hmap_next_in_bucket(ITER_VAR(NODE))))
 
 static inline struct hmap_node *hmap_first_with_hash(const struct hmap *,
                                                      size_t hash);
@@ -170,54 +170,80 @@ bool hmap_contains(const struct hmap *, const struct hmap_node *);
 /* Iterates through every node in HMAP. */
 #define HMAP_FOR_EACH(NODE, MEMBER, HMAP) \
     HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, (void) 0)
-#define HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, ...)                     \
-    for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER), __VA_ARGS__;   \
-         (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))                \
-         || ((NODE = NULL), false);                                     \
-         ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER))
+#define HMAP_FOR_EACH_INIT(NODE, MEMBER, HMAP, ...)                           \
+    for (INIT_MULTIVAR_EXP(NODE, MEMBER, hmap_first(HMAP), struct hmap_node,  \
+                           __VA_ARGS__);                                      \
+         CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL);            \
+         UPDATE_MULTIVAR(NODE, hmap_next(HMAP, ITER_VAR(NODE))))
 
 /* Safe when NODE may be freed (not needed when NODE may be removed from the
  * hash map but its members remain accessible and intact). */
-#define HMAP_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HMAP) \
-    HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, MEMBER, HMAP, (void) 0)
-#define HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, MEMBER, HMAP, ...)          \
-    for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER), __VA_ARGS__;   \
-         ((NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))               \
-          || ((NODE = NULL), false)                                     \
-          ? INIT_CONTAINER(NEXT, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER), 1 \
-          : 0);                                                         \
-         (NODE) = (NEXT))
+#define HMAP_FOR_EACH_SAFE_LONG(NODE, NEXT, MEMBER, HMAP) \
+    HMAP_FOR_EACH_SAFE_LONG_INIT (NODE, NEXT, MEMBER, HMAP, (void) NEXT)
+
+#define HMAP_FOR_EACH_SAFE_LONG_INIT(NODE, NEXT, MEMBER, HMAP, ...)           \
+    for (INIT_MULTIVAR_SAFE_LONG_EXP(NODE, NEXT, MEMBER, hmap_first(HMAP),    \
+                                     struct hmap_node, __VA_ARGS__);          \
+         CONDITION_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER,                     \
+                                      ITER_VAR(NODE) != NULL,                 \
+                            ITER_VAR(NEXT) = hmap_next(HMAP, ITER_VAR(NODE)), \
+                                      ITER_VAR(NEXT) != NULL);                \
+         UPDATE_MULTIVAR_SAFE_LONG(NODE, NEXT))
+
+/* Short versions of HMAP_FOR_EACH_SAFE. */
+#define HMAP_FOR_EACH_SAFE_SHORT(NODE, MEMBER, HMAP)                          \
+    HMAP_FOR_EACH_SAFE_SHORT_INIT (NODE, MEMBER, HMAP, (void) 0)
+
+#define HMAP_FOR_EACH_SAFE_SHORT_INIT(NODE, MEMBER, HMAP, ...)                \
+    for (INIT_MULTIVAR_SAFE_SHORT_EXP(NODE, MEMBER, hmap_first(HMAP),         \
+                                      struct hmap_node, __VA_ARGS__);         \
+         CONDITION_MULTIVAR_SAFE_SHORT(NODE, MEMBER,                          \
+                                       ITER_VAR(NODE) != NULL,                \
+                      ITER_NEXT_VAR(NODE) = hmap_next(HMAP, ITER_VAR(NODE))); \
+         UPDATE_MULTIVAR_SAFE_SHORT(NODE))
+
+#define HMAP_FOR_EACH_SAFE(...)                                               \
+    OVERLOAD_SAFE_MACRO(HMAP_FOR_EACH_SAFE_LONG,                              \
+                        HMAP_FOR_EACH_SAFE_SHORT,                             \
+                        4, __VA_ARGS__)
+
 
 /* Continues an iteration from just after NODE. */
 #define HMAP_FOR_EACH_CONTINUE(NODE, MEMBER, HMAP) \
     HMAP_FOR_EACH_CONTINUE_INIT(NODE, MEMBER, HMAP, (void) 0)
-#define HMAP_FOR_EACH_CONTINUE_INIT(NODE, MEMBER, HMAP, ...)            \
-    for (ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER), \
-         __VA_ARGS__;                                                   \
-         (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))                \
-         || ((NODE = NULL), false);                                     \
-         ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER))
+#define HMAP_FOR_EACH_CONTINUE_INIT(NODE, MEMBER, HMAP, ...)                  \
+    for (INIT_MULTIVAR_EXP(NODE, MEMBER, hmap_next(HMAP, &(NODE)->MEMBER),    \
+                           struct hmap_node, __VA_ARGS__);                    \
+         CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL);            \
+         UPDATE_MULTIVAR(NODE, hmap_next(HMAP, ITER_VAR(NODE))))
+
+struct hmap_pop_helper_iter__ {
+    size_t bucket;
+    struct hmap_node *node;
+};
 
-static inline struct hmap_node *
-hmap_pop_helper__(struct hmap *hmap, size_t *bucket) {
+static inline void
+hmap_pop_helper__(struct hmap *hmap, struct hmap_pop_helper_iter__ *iter) {
 
-    for (; *bucket <= hmap->mask; (*bucket)++) {
-        struct hmap_node *node = hmap->buckets[*bucket];
+    for (; iter->bucket <= hmap->mask; (iter->bucket)++) {
+        struct hmap_node *node = hmap->buckets[iter->bucket];
 
         if (node) {
             hmap_remove(hmap, node);
-            return node;
+            iter->node = node;
+            return;
         }
     }
-
-    return NULL;
+    iter->node = NULL;
 }
 
-#define HMAP_FOR_EACH_POP(NODE, MEMBER, HMAP)                               \
-    for (size_t bucket__ = 0;                                               \
-         INIT_CONTAINER(NODE, hmap_pop_helper__(HMAP, &bucket__), MEMBER),  \
-         (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER))                    \
-         || ((NODE = NULL), false);)
+#define HMAP_FOR_EACH_POP(NODE, MEMBER, HMAP)                                 \
+    for (struct hmap_pop_helper_iter__ ITER_VAR(NODE) = { 0, NULL };          \
+         hmap_pop_helper__(HMAP, &ITER_VAR(NODE)),                            \
+         (ITER_VAR(NODE).node != NULL) ?                                      \
+            (((NODE) = OBJECT_CONTAINING(ITER_VAR(NODE).node,                 \
+                                         NODE, MEMBER)),1):                   \
+            (((NODE) = NULL), 0);)
 
 static inline struct hmap_node *hmap_first(const struct hmap *);
 static inline struct hmap_node *hmap_next(const struct hmap *,
diff --git a/include/openvswitch/list.h b/include/openvswitch/list.h
index 8ad5eeb327..6272d340cf 100644
--- a/include/openvswitch/list.h
+++ b/include/openvswitch/list.h
@@ -72,37 +72,74 @@ static inline bool ovs_list_is_empty(const struct ovs_list *);
 static inline bool ovs_list_is_singleton(const struct ovs_list *);
 static inline bool ovs_list_is_short(const struct ovs_list *);
 
-#define LIST_FOR_EACH(ITER, MEMBER, LIST)                               \
-    for (INIT_CONTAINER(ITER, (LIST)->next, MEMBER);                    \
-         &(ITER)->MEMBER != (LIST);                                     \
-         ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER))
-#define LIST_FOR_EACH_CONTINUE(ITER, MEMBER, LIST)                      \
-    for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER);             \
-         &(ITER)->MEMBER != (LIST);                                     \
-         ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER))
-#define LIST_FOR_EACH_REVERSE(ITER, MEMBER, LIST)                       \
-    for (INIT_CONTAINER(ITER, (LIST)->prev, MEMBER);                    \
-         &(ITER)->MEMBER != (LIST);                                     \
-         ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER))
-#define LIST_FOR_EACH_REVERSE_SAFE(ITER, PREV, MEMBER, LIST)        \
-    for (INIT_CONTAINER(ITER, (LIST)->prev, MEMBER);                \
-         (&(ITER)->MEMBER != (LIST)                                 \
-          ? INIT_CONTAINER(PREV, (ITER)->MEMBER.prev, MEMBER), 1    \
-          : 0);                                                     \
-         (ITER) = (PREV))
-#define LIST_FOR_EACH_REVERSE_CONTINUE(ITER, MEMBER, LIST)              \
-    for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER);           \
-         &(ITER)->MEMBER != (LIST);                                     \
-         ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER))
-#define LIST_FOR_EACH_SAFE(ITER, NEXT, MEMBER, LIST)               \
-    for (INIT_CONTAINER(ITER, (LIST)->next, MEMBER);               \
-         (&(ITER)->MEMBER != (LIST)                                \
-          ? INIT_CONTAINER(NEXT, (ITER)->MEMBER.next, MEMBER), 1   \
-          : 0);                                                    \
-         (ITER) = (NEXT))
-#define LIST_FOR_EACH_POP(ITER, MEMBER, LIST)                      \
-    while (!ovs_list_is_empty(LIST)                                    \
-           && (INIT_CONTAINER(ITER, ovs_list_pop_front(LIST), MEMBER), 1))
+#define LIST_FOR_EACH(VAR, MEMBER, LIST)                                      \
+    for (INIT_MULTIVAR(VAR, MEMBER, (LIST)->next, struct ovs_list);           \
+         CONDITION_MULTIVAR(VAR, MEMBER, ITER_VAR(VAR) != (LIST));            \
+         UPDATE_MULTIVAR(VAR, ITER_VAR(VAR)->next))
+
+#define LIST_FOR_EACH_CONTINUE(VAR, MEMBER, LIST)                             \
+    for (INIT_MULTIVAR(VAR, MEMBER, VAR->MEMBER.next, struct ovs_list);       \
+         CONDITION_MULTIVAR(VAR, MEMBER, ITER_VAR(VAR) != (LIST));            \
+         UPDATE_MULTIVAR(VAR, ITER_VAR(VAR)->next))
+
+#define LIST_FOR_EACH_REVERSE(VAR, MEMBER, LIST)                              \
+    for (INIT_MULTIVAR(VAR, MEMBER, (LIST)->prev, struct ovs_list);           \
+         CONDITION_MULTIVAR(VAR, MEMBER, ITER_VAR(VAR) != (LIST));            \
+         UPDATE_MULTIVAR(VAR, ITER_VAR(VAR)->prev))
+
+#define LIST_FOR_EACH_REVERSE_CONTINUE(VAR, MEMBER, LIST)                     \
+    for (INIT_MULTIVAR(VAR, MEMBER, VAR->MEMBER.prev, struct ovs_list);       \
+         CONDITION_MULTIVAR(VAR, MEMBER, ITER_VAR(VAR) != (LIST));            \
+         UPDATE_MULTIVAR(VAR, ITER_VAR(VAR)->prev))
+
+/* LONG version of SAFE iterators. */
+#define LIST_FOR_EACH_REVERSE_SAFE_LONG(VAR, PREV, MEMBER, LIST)              \
+    for (INIT_MULTIVAR_SAFE_LONG(VAR, PREV, MEMBER, (LIST)->prev,             \
+                                 struct ovs_list);                            \
+         CONDITION_MULTIVAR_SAFE_LONG(VAR, PREV, MEMBER,                      \
+                                      ITER_VAR(VAR) != (LIST),                \
+                                      ITER_VAR(PREV) = ITER_VAR(VAR)->prev,   \
+                                      ITER_VAR(PREV) != (LIST));              \
+         UPDATE_MULTIVAR_SAFE_LONG(VAR, PREV))
+
+#define LIST_FOR_EACH_SAFE_LONG(VAR, NEXT, MEMBER, LIST)                      \
+    for (INIT_MULTIVAR_SAFE_LONG(VAR, NEXT, MEMBER, (LIST)->next,             \
+                                 struct ovs_list);                            \
+         CONDITION_MULTIVAR_SAFE_LONG(VAR, NEXT, MEMBER,                      \
+                                      ITER_VAR(VAR) != (LIST),                \
+                                      ITER_VAR(NEXT) = ITER_VAR(VAR)->next,   \
+                                      ITER_VAR(NEXT) != (LIST));              \
+         UPDATE_MULTIVAR_SAFE_LONG(VAR, NEXT))
+
+/* SHORT version of SAFE iterators. */
+#define LIST_FOR_EACH_REVERSE_SAFE_SHORT(VAR, MEMBER, LIST)                   \
+    for (INIT_MULTIVAR_SAFE_SHORT(VAR, MEMBER, (LIST)->prev, struct ovs_list);\
+         CONDITION_MULTIVAR_SAFE_SHORT(VAR, MEMBER,                           \
+                                       ITER_VAR(VAR) != (LIST),               \
+                                 ITER_NEXT_VAR(VAR) = ITER_VAR(VAR)->prev);   \
+         UPDATE_MULTIVAR_SAFE_SHORT(VAR))
+
+#define LIST_FOR_EACH_SAFE_SHORT(VAR, MEMBER, LIST)                           \
+    for (INIT_MULTIVAR_SAFE_SHORT(VAR, MEMBER, (LIST)->next, struct ovs_list);\
+         CONDITION_MULTIVAR_SAFE_SHORT(VAR, MEMBER,                           \
+                                       ITER_VAR(VAR) != (LIST),               \
+                                 ITER_NEXT_VAR(VAR) = ITER_VAR(VAR)->next);   \
+         UPDATE_MULTIVAR_SAFE_SHORT(VAR))
+
+#define LIST_FOR_EACH_SAFE(...)                      \
+    OVERLOAD_SAFE_MACRO(LIST_FOR_EACH_SAFE_LONG,     \
+                        LIST_FOR_EACH_SAFE_SHORT,    \
+                        4, __VA_ARGS__)
+
+#define LIST_FOR_EACH_REVERSE_SAFE(...)                        \
+    OVERLOAD_SAFE_MACRO(LIST_FOR_EACH_REVERSE_SAFE_LONG,       \
+                        LIST_FOR_EACH_REVERSE_SAFE_SHORT,      \
+                        4, __VA_ARGS__)
+
+#define LIST_FOR_EACH_POP(ITER, MEMBER, LIST)                                 \
+    while (!ovs_list_is_empty(LIST) ?                                         \
+           (INIT_CONTAINER(ITER, ovs_list_pop_front(LIST), MEMBER), 1) :      \
+           (ITER = NULL, 0))
 
 /* Inline implementations. */
 
diff --git a/include/openvswitch/shash.h b/include/openvswitch/shash.h
index c249e13e1f..4e7badd4dc 100644
--- a/include/openvswitch/shash.h
+++ b/include/openvswitch/shash.h
@@ -41,13 +41,24 @@ struct shash {
                         BUILD_ASSERT_TYPE(SHASH_NODE, struct shash_node *), \
                         BUILD_ASSERT_TYPE(SHASH, struct shash *))
 
-#define SHASH_FOR_EACH_SAFE(SHASH_NODE, NEXT, SHASH)        \
-    HMAP_FOR_EACH_SAFE_INIT (                               \
+#define SHASH_FOR_EACH_SAFE_SHORT(SHASH_NODE, SHASH)        \
+    HMAP_FOR_EACH_SAFE_SHORT_INIT (                         \
+        SHASH_NODE, node, &(SHASH)->map,                    \
+        BUILD_ASSERT_TYPE(SHASH_NODE, struct shash_node *), \
+        BUILD_ASSERT_TYPE(SHASH, struct shash *))
+
+#define SHASH_FOR_EACH_SAFE_LONG(SHASH_NODE, NEXT, SHASH)   \
+    HMAP_FOR_EACH_SAFE_LONG_INIT (                          \
         SHASH_NODE, NEXT, node, &(SHASH)->map,              \
         BUILD_ASSERT_TYPE(SHASH_NODE, struct shash_node *), \
         BUILD_ASSERT_TYPE(NEXT, struct shash_node *),       \
         BUILD_ASSERT_TYPE(SHASH, struct shash *))
 
+#define SHASH_FOR_EACH_SAFE(...)                                              \
+    OVERLOAD_SAFE_MACRO(SHASH_FOR_EACH_SAFE_LONG,                             \
+                        SHASH_FOR_EACH_SAFE_SHORT,                            \
+                        3, __VA_ARGS__)
+
 void shash_init(struct shash *);
 void shash_destroy(struct shash *);
 void shash_destroy_free_data(struct shash *);
diff --git a/include/openvswitch/util.h b/include/openvswitch/util.h
index 228b185c3a..96f600160b 100644
--- a/include/openvswitch/util.h
+++ b/include/openvswitch/util.h
@@ -145,6 +145,150 @@ OVS_NO_RETURN void ovs_assert_failure(const char *, const char *, const char *);
 #define INIT_CONTAINER(OBJECT, POINTER, MEMBER) \
     ((OBJECT) = NULL, ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER))
 
+/* Multi-variable container iterators.
+ *
+ * The following macros facilitate safe iteration over data structures
+ * contained in objects. It does so by using an internal iterator variable of
+ * the type of the member object pointer (i.e: pointer to the data structure).
+ */
+
+/* Multi-variable iterator variable name.
+ * Returns the name of the internal iterator variable.
+ */
+#define ITER_VAR(NAME) NAME ## __iterator__
+
+/* Multi-variable initialization. Creates an internal iterator variable that
+ * points to the provided pointer. The type of the iterator variable is
+ * ITER_TYPE*. It must be the same type as &VAR->MEMBER.
+ *
+ * The _EXP version evaluates the extra expressions once.
+ */
+#define INIT_MULTIVAR(VAR, MEMBER, POINTER, ITER_TYPE)                  \
+    INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, (void) 0)
+
+#define INIT_MULTIVAR_EXP(VAR, MEMBER, POINTER, ITER_TYPE, ...)         \
+    ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER)
+
+/* Multi-variable condition.
+ * Evaluates the condition expression (that must be based on the internal
+ * iterator variable). Only if the result of expression is true, the OBJECT is
+ * set to the object containing the current value of the iterator variable.
+ *
+ * It is up to the caller to make sure it is safe to run OBJECT_CONTAINING on
+ * the pointers that verify the condition.
+ */
+#define CONDITION_MULTIVAR(VAR, MEMBER, EXPR)                                 \
+    ((EXPR) ?                                                                 \
+     (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)), 1) :           \
+     (((VAR) = NULL), 0))
+
+/* Multi-variable update.
+ * Sets the iterator value to NEXT_ITER.
+ */
+#define UPDATE_MULTIVAR(VAR, NEXT_ITER)                                       \
+    (ITER_VAR(VAR) = NEXT_ITER)
+
+/* In the safe version of the multi-variable container iteration, the next
+ * value of the iterator is precalculated on the condition expression.
+ * This allows for the iterator to be freed inside the loop.
+ *
+ * Two versions of the macros are provided:
+ *
+ * * In the _SHORT version, the user does not have to provide a variable to
+ * store the next value of the iterator. Instead, a second iterator variable
+ * is declared in the INIT_ macro and its name is determined by
+ * ITER_NEXT_VAR(OBJECT).
+ *
+ * * In the _LONG version, the user provides another variable of the same type
+ * as the iterator object variable to store the next containing object.
+ * We still declare an iterator variable inside the loop but in this case it's
+ * name is derived from the name of the next containing variable.
+ * The value of the next containing object will only be set
+ * (via OBJECT_CONTAINING) if an additional condition is statisfied. This
+ * second condition must ensure it is safe to call OBJECT_CONTAINING on the
+ * next iterator variable.
+ * With respect to the value of the next containing object:
+ *  - Inside of the loop: the variable is either NULL or safe to use.
+ *  - Outside of the loop: the variable is NULL if the loop ends normally.
+ *     If the loop ends with a "break;" statement, rules of Inside the loop
+ *     apply.
+ */
+#define ITER_NEXT_VAR(NAME) NAME ## __iterator__next__
+
+/* Safe initialization declares both iterators. */
+#define INIT_MULTIVAR_SAFE_SHORT(VAR, MEMBER, POINTER, ITER_TYPE)             \
+    INIT_MULTIVAR_SAFE_SHORT_EXP(VAR, MEMBER, POINTER, ITER_TYPE, (void) 0)
+
+#define INIT_MULTIVAR_SAFE_SHORT_EXP(VAR, MEMBER, POINTER, ITER_TYPE, ...)    \
+    ITER_TYPE *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER),        \
+        *ITER_NEXT_VAR(VAR) = NULL
+
+/* Evaluate the condition expression and, if satisfied, update the _next_
+ * iterator with the NEXT_EXPR.
+ * Both EXPR and NEXT_EXPR should only use ITER_VAR(VAR) and
+ * ITER_NEXT_VAR(VAR).
+ */
+#define CONDITION_MULTIVAR_SAFE_SHORT(VAR, MEMBER, EXPR, NEXT_EXPR)           \
+    ((EXPR) ?                                                                 \
+     (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)),                \
+      (NEXT_EXPR), 1) :                                                       \
+     (((VAR) = NULL), 0))
+
+#define UPDATE_MULTIVAR_SAFE_SHORT(VAR)                                       \
+    UPDATE_MULTIVAR(VAR, ITER_NEXT_VAR(VAR))
+
+/* _LONG versions of the macros. */
+
+#define INIT_MULTIVAR_SAFE_LONG(VAR, NEXT_VAR, MEMBER, POINTER, ITER_TYPE)    \
+    INIT_MULTIVAR_SAFE_LONG_EXP(VAR, NEXT_VAR, MEMBER, POINTER, ITER_TYPE,    \
+                                (void) 0)                                     \
+
+#define INIT_MULTIVAR_SAFE_LONG_EXP(VAR, NEXT_VAR, MEMBER, POINTER,           \
+                                    ITER_TYPE, ...)                           \
+    ITER_TYPE  *ITER_VAR(VAR) = ( __VA_ARGS__ , (ITER_TYPE *) POINTER),       \
+        *ITER_VAR(NEXT_VAR) = NULL
+
+/* Evaluate the condition expression and, if satisfied, update the _next_
+ * iterator with the NEXT_EXPR. After, evaluate the NEXT_COND and, if
+ * satisfied, set the value to NEXT_VAR. NEXT_COND must use ITER_VAR(NEXT_VAR).
+ *
+ * Both EXPR and NEXT_EXPR should only use ITER_VAR(VAR) and
+ * ITER_VAR(NEXT_VAR).
+ */
+#define CONDITION_MULTIVAR_SAFE_LONG(VAR, NEXT_VAR, MEMBER, EXPR, NEXT_EXPR,  \
+                                     NEXT_COND)                               \
+    ((EXPR) ?                                                                 \
+     (((VAR) = OBJECT_CONTAINING(ITER_VAR(VAR), VAR, MEMBER)),                \
+      (NEXT_EXPR), ((NEXT_COND) ?                                             \
+       ((NEXT_VAR) =                                                          \
+        OBJECT_CONTAINING(ITER_VAR(NEXT_VAR), NEXT_VAR, MEMBER)) :            \
+       ((NEXT_VAR) = NULL)), 1) :                                             \
+     (((VAR) = NULL), ((NEXT_VAR) = NULL), 0))
+
+#define UPDATE_MULTIVAR_SAFE_LONG(VAR, NEXT_VAR)                              \
+    UPDATE_MULTIVAR(VAR, ITER_VAR(NEXT_VAR))
+
+/* Helpers to allow overloading the *_SAFE iterator macros and select either
+ * the LONG or the SHORT version depending on the number of arguments.
+ */
+#define GET_SAFE_MACRO2(_1, _2, NAME, ...) NAME
+#define GET_SAFE_MACRO3(_1, _2, _3, NAME, ...) NAME
+#define GET_SAFE_MACRO4(_1, _2, _3, _4, NAME, ...) NAME
+#define GET_SAFE_MACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME
+#define GET_SAFE_MACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
+#define GET_SAFE_MACRO(MAX_ARGS) GET_SAFE_MACRO ## MAX_ARGS
+
+/* MSVC treats __VA_ARGS__ as a simple token in argument lists. Introduce
+ * a level of indirection to work around that. */
+#define EXPAND_MACRO(name, args) name args
+
+/* Overload the LONG and the SHORT version of the macros. MAX_ARGS is the
+ * maximum number of arguments (i.e: the number of arguments of the LONG
+ * version). */
+#define OVERLOAD_SAFE_MACRO(LONG, SHORT, MAX_ARGS, ...) \
+        EXPAND_MACRO(GET_SAFE_MACRO(MAX_ARGS), \
+                     (__VA_ARGS__, LONG, SHORT))(__VA_ARGS__)
+
 /* Returns the number of elements in ARRAY. */
 #define ARRAY_SIZE(ARRAY) __ARRAY_SIZE(ARRAY)
 
diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
index a8b0705d9f..631a8fca80 100755
--- a/ipsec/ovs-monitor-ipsec.in
+++ b/ipsec/ovs-monitor-ipsec.in
@@ -337,7 +337,14 @@ conn prevent_unencrypted_vxlan
         Once strongSwan vici bindings will be distributed with major
         Linux distributions this function could be simplified."""
         vlog.info("Refreshing StrongSwan configuration")
-        subprocess.call([self.IPSEC, "update"])
+        proc = subprocess.Popen([self.IPSEC, "update"],
+                        stdout=subprocess.PIPE,
+                        stderr=subprocess.PIPE)
+        outs, errs = proc.communicate()
+        if proc.returncode != 0:
+            vlog.err("StrongSwan failed to update configuration:\n"
+                           "%s \n %s" % (str(outs), str(errs)))
+
         subprocess.call([self.IPSEC, "rereadsecrets"])
         # "ipsec update" command does not remove those tunnels that were
         # updated or that disappeared from the ipsec.conf file.  So, we have
@@ -708,6 +715,11 @@ conn prevent_unencrypted_vxlan
                     not re.match(r".*need --listen.*", pout):
                 break
 
+        if re.match(r".*[F|f]ailed to initiate connection.*", pout):
+            vlog.err('Failed to initiate connection through'
+                    ' Interface %s.\n' % (conn.split('-')[0]))
+            vlog.err(pout)
+
     def _nss_clear_database(self):
         """Remove all OVS IPsec related state from the NSS database"""
         try:
diff --git a/lib/cfm.c b/lib/cfm.c
index cc43e70e31..c3742f3de2 100644
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -416,7 +416,7 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
     ovs_mutex_lock(&mutex);
     if (timer_expired(&cfm->fault_timer)) {
         long long int interval = cfm_fault_interval(cfm);
-        struct remote_mp *rmp, *rmp_next;
+        struct remote_mp *rmp;
         enum cfm_fault_reason old_cfm_fault = cfm->fault;
         uint64_t old_flap_count = cfm->flap_count;
         int old_health = cfm->health;
@@ -475,7 +475,7 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
             cfm->rx_packets = rx_packets;
         }
 
-        HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) {
+        HMAP_FOR_EACH_SAFE (rmp, node, &cfm->remote_mps) {
             if (!rmp->recv) {
                 VLOG_INFO("%s: Received no CCM from RMP %"PRIu64" in the last"
                           " %lldms", cfm->name, rmp->mpid,
diff --git a/lib/classifier.c b/lib/classifier.c
index c4790ee6ba..0a89626cc3 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -916,9 +916,9 @@ free_conjunctive_matches(struct hmap *matches,
                          struct conjunctive_match *cm_stubs, size_t n_cm_stubs)
 {
     if (hmap_count(matches) > n_cm_stubs) {
-        struct conjunctive_match *cm, *next;
+        struct conjunctive_match *cm;
 
-        HMAP_FOR_EACH_SAFE (cm, next, hmap_node, matches) {
+        HMAP_FOR_EACH_SAFE (cm, hmap_node, matches) {
             if (!(cm >= cm_stubs && cm < &cm_stubs[n_cm_stubs])) {
                 free(cm);
             }
diff --git a/lib/cmap.h b/lib/cmap.h
index c502d23112..72e2ec5f71 100644
--- a/lib/cmap.h
+++ b/lib/cmap.h
@@ -108,6 +108,8 @@ size_t cmap_replace(struct cmap *, struct cmap_node *old_node,
  *
  * CMAP and HASH are evaluated only once.  NODE is evaluated many times.
  *
+ * After a normal exit of the loop (not through a "break;" statement) NODE is
+ * NULL.
  *
  * Thread-safety
  * =============
@@ -128,15 +130,15 @@ size_t cmap_replace(struct cmap *, struct cmap_node *old_node,
  * CMAP_FOR_EACH_WITH_HASH_PROTECTED may only be used if CMAP is guaranteed not
  * to change during iteration.  It may be very slightly faster.
  */
-#define CMAP_NODE_FOR_EACH(NODE, MEMBER, CMAP_NODE)                     \
-    for (INIT_CONTAINER(NODE, CMAP_NODE, MEMBER);                       \
-         (NODE) != OBJECT_CONTAINING(NULL, NODE, MEMBER);               \
-         ASSIGN_CONTAINER(NODE, cmap_node_next(&(NODE)->MEMBER), MEMBER))
-#define CMAP_NODE_FOR_EACH_PROTECTED(NODE, MEMBER, CMAP_NODE)           \
-    for (INIT_CONTAINER(NODE, CMAP_NODE, MEMBER);                       \
-         (NODE) != OBJECT_CONTAINING(NULL, NODE, MEMBER);               \
-         ASSIGN_CONTAINER(NODE, cmap_node_next_protected(&(NODE)->MEMBER), \
-                          MEMBER))
+#define CMAP_NODE_FOR_EACH(NODE, MEMBER, CMAP_NODE)                        \
+    for (INIT_MULTIVAR(NODE, MEMBER, CMAP_NODE, struct cmap_node);         \
+         CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL);         \
+         UPDATE_MULTIVAR(NODE, cmap_node_next(ITER_VAR(NODE))))
+#define CMAP_NODE_FOR_EACH_PROTECTED(NODE, MEMBER, CMAP_NODE)              \
+    for (INIT_MULTIVAR(NODE, MEMBER, CMAP_NODE, struct cmap_node);         \
+         CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL);         \
+         UPDATE_MULTIVAR(NODE, cmap_node_next_protected(ITER_VAR(NODE))))
+
 #define CMAP_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, CMAP)   \
     CMAP_NODE_FOR_EACH(NODE, MEMBER, cmap_find(CMAP, HASH))
 #define CMAP_FOR_EACH_WITH_HASH_PROTECTED(NODE, MEMBER, HASH, CMAP)     \
@@ -223,7 +225,7 @@ unsigned long cmap_find_batch(const struct cmap *cmap, unsigned long map,
      ? (INIT_CONTAINER(NODE, (CURSOR)->node, MEMBER),   \
         cmap_cursor_advance(CURSOR),                    \
         true)                                           \
-     : false)
+     : (NODE = NULL, false))
 
 #define CMAP_CURSOR_FOR_EACH(NODE, MEMBER, CURSOR, CMAP)    \
     for (*(CURSOR) = cmap_cursor_start(CMAP);               \
diff --git a/lib/conntrack.c b/lib/conntrack.c
index 33a1a92953..08da4ddf79 100644
--- a/lib/conntrack.c
+++ b/lib/conntrack.c
@@ -1526,14 +1526,14 @@ set_label(struct dp_packet *pkt, struct conn *conn,
 static long long
 ct_sweep(struct conntrack *ct, long long now, size_t limit)
 {
-    struct conn *conn, *next;
+    struct conn *conn;
     long long min_expiration = LLONG_MAX;
     size_t count = 0;
 
     ovs_mutex_lock(&ct->ct_lock);
 
     for (unsigned i = 0; i < N_CT_TM; i++) {
-        LIST_FOR_EACH_SAFE (conn, next, exp_node, &ct->exp_lists[i]) {
+        LIST_FOR_EACH_SAFE (conn, exp_node, &ct->exp_lists[i]) {
             ovs_mutex_lock(&conn->lock);
             if (now < conn->expiration || count >= limit) {
                 min_expiration = MIN(min_expiration, conn->expiration);
@@ -2265,8 +2265,16 @@ set_sport_range(const struct nat_action_info_t *ni, const struct conn_key *k,
     if (((ni->nat_action & NAT_ACTION_SNAT_ALL) == NAT_ACTION_SRC) ||
         ((ni->nat_action & NAT_ACTION_DST))) {
         *curr = ntohs(k->src.port);
-        *min = MIN_NAT_EPHEMERAL_PORT;
-        *max = MAX_NAT_EPHEMERAL_PORT;
+        if (*curr < 512) {
+            *min = 1;
+            *max = 511;
+        } else if (*curr < 1024) {
+            *min = 600;
+            *max = 1023;
+        } else {
+            *min = MIN_NAT_EPHEMERAL_PORT;
+            *max = MAX_NAT_EPHEMERAL_PORT;
+        }
     } else {
         *min = ni->min_port;
         *max = ni->max_port;
@@ -2389,6 +2397,26 @@ next_addr_in_range_guarded(union ct_addr *curr, union ct_addr *min,
     return exhausted;
 }
 
+static bool
+nat_get_unique_l4(struct conntrack *ct, struct conn *nat_conn,
+                  ovs_be16 *port, uint16_t curr, uint16_t min,
+                  uint16_t max)
+{
+    uint16_t orig = curr;
+
+    FOR_EACH_PORT_IN_RANGE (curr, min, max) {
+        *port = htons(curr);
+        if (!conn_lookup(ct, &nat_conn->rev_key,
+                         time_msec(), NULL, NULL)) {
+            return true;
+        }
+    }
+
+    *port = htons(orig);
+
+    return false;
+}
+
 /* This function tries to get a unique tuple.
  * Every iteration checks that the reverse tuple doesn't
  * collide with any existing one.
@@ -2403,9 +2431,11 @@ next_addr_in_range_guarded(union ct_addr *curr, union ct_addr *min,
  *
  * In case of DNAT:
  *    - For each dst IP address in the range (if any).
- *        - For each dport in range (if any).
- *             - Try to find a source port in the ephemeral range
- *               (after testing the port used by the sender).
+ *        - For each dport in range (if any) tries to find
+ *          an unique tuple.
+ *        - Eventually, if the previous attempt fails,
+ *          tries to find a source port in the ephemeral
+ *          range (after testing the port used by the sender).
  *
  * If none can be found, return exhaustion to the caller. */
 static bool
@@ -2436,6 +2466,11 @@ nat_get_unique_tuple(struct conntrack *ct, const struct conn *conn,
     set_dport_range(nat_info, &conn->key, hash, &curr_dport,
                     &min_dport, &max_dport);
 
+    if (pat_proto) {
+        nat_conn->rev_key.src.port = htons(curr_dport);
+        nat_conn->rev_key.dst.port = htons(curr_sport);
+    }
+
 another_round:
     store_addr_to_key(&curr_addr, &nat_conn->rev_key,
                       nat_info->nat_action);
@@ -2449,15 +2484,19 @@ another_round:
         goto next_addr;
     }
 
-    FOR_EACH_PORT_IN_RANGE(curr_dport, min_dport, max_dport) {
-        nat_conn->rev_key.src.port = htons(curr_dport);
-        FOR_EACH_PORT_IN_RANGE(curr_sport, min_sport, max_sport) {
-            nat_conn->rev_key.dst.port = htons(curr_sport);
-            if (!conn_lookup(ct, &nat_conn->rev_key,
-                             time_msec(), NULL, NULL)) {
-                return true;
-            }
-        }
+    bool found = false;
+    if (nat_info->nat_action & NAT_ACTION_DST_PORT) {
+        found = nat_get_unique_l4(ct, nat_conn, &nat_conn->rev_key.src.port,
+                                  curr_dport, min_dport, max_dport);
+    }
+
+    if (!found) {
+        found = nat_get_unique_l4(ct, nat_conn, &nat_conn->rev_key.dst.port,
+                                  curr_sport, min_sport, max_sport);
+    }
+
+    if (found) {
+        return true;
     }
 
     /* Check if next IP is in range and respin. Otherwise, notify
@@ -2857,8 +2896,8 @@ expectation_clean(struct conntrack *ct, const struct conn_key *parent_key)
 {
     ovs_rwlock_wrlock(&ct->resources_lock);
 
-    struct alg_exp_node *node, *next;
-    HINDEX_FOR_EACH_WITH_HASH_SAFE (node, next, node_ref,
+    struct alg_exp_node *node;
+    HINDEX_FOR_EACH_WITH_HASH_SAFE (node, node_ref,
                                     conn_key_hash(parent_key, ct->hash_basis),
                                     &ct->alg_expectation_refs) {
         if (!conn_key_cmp(&node->parent_key, parent_key)) {
diff --git a/lib/dns-resolve.c b/lib/dns-resolve.c
index d344514343..1afcc65adb 100644
--- a/lib/dns-resolve.c
+++ b/lib/dns-resolve.c
@@ -189,8 +189,8 @@ dns_resolve_destroy(void)
         ub_ctx_delete(ub_ctx__);
         ub_ctx__ = NULL;
 
-        struct resolve_request *req, *next;
-        HMAP_FOR_EACH_SAFE (req, next, hmap_node, &all_reqs__) {
+        struct resolve_request *req;
+        HMAP_FOR_EACH_SAFE (req, hmap_node, &all_reqs__) {
             ub_resolve_free(req->ub_result);
             free(req->addr);
             free(req->name);
@@ -265,7 +265,7 @@ resolve_callback__(void *req_, int err, struct ub_result *result)
     if (err != 0 || (result->qtype == ns_t_aaaa && !result->havedata)) {
         ub_resolve_free(result);
         req->state = RESOLVE_ERROR;
-        VLOG_ERR_RL(&rl, "%s: failed to resolve", req->name);
+        VLOG_WARN_RL(&rl, "%s: failed to resolve", req->name);
         return;
     }
 
diff --git a/lib/dpif-netdev-avx512.c b/lib/dpif-netdev-avx512.c
index b7131ba3f1..11d9a00052 100644
--- a/lib/dpif-netdev-avx512.c
+++ b/lib/dpif-netdev-avx512.c
@@ -159,7 +159,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd,
         mf_mask = mfex_func(packets, keys, batch_size, in_port, pmd);
     }
 
-    uint32_t lookup_pkts_bitmask = (1ULL << batch_size) - 1;
+    uint32_t lookup_pkts_bitmask = (UINT64_C(1) << batch_size) - 1;
     uint32_t iter = lookup_pkts_bitmask;
     while (iter) {
         uint32_t i = raw_ctz(iter);
@@ -183,7 +183,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd,
          * classifed by vector mfex else do a scalar miniflow extract
          * for that packet.
          */
-        bool mfex_hit = !!(mf_mask & (1 << i));
+        bool mfex_hit = !!(mf_mask & (UINT32_C(1) << i));
 
         /* Check for a partial hardware offload match. */
         if (hwol_enabled) {
@@ -204,7 +204,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd,
 
                 pkt_meta[i].bytes = dp_packet_size(packet);
                 phwol_hits++;
-                hwol_emc_smc_hitmask |= (1 << i);
+                hwol_emc_smc_hitmask |= (UINT32_C(1) << i);
                 continue;
             }
         }
@@ -227,7 +227,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd,
             if (f) {
                 rules[i] = &f->cr;
                 emc_hits++;
-                hwol_emc_smc_hitmask |= (1 << i);
+                hwol_emc_smc_hitmask |= (UINT32_C(1) << i);
                 continue;
             }
         }
@@ -237,7 +237,7 @@ dp_netdev_input_outer_avx512(struct dp_netdev_pmd_thread *pmd,
             if (f) {
                 rules[i] = &f->cr;
                 smc_hits++;
-                smc_hitmask |= (1 << i);
+                smc_hitmask |= (UINT32_C(1) << i);
                 continue;
             }
         }
diff --git a/lib/dpif-netdev-extract-avx512.c b/lib/dpif-netdev-extract-avx512.c
index c1c1fefb6a..a0fedb1376 100644
--- a/lib/dpif-netdev-extract-avx512.c
+++ b/lib/dpif-netdev-extract-avx512.c
@@ -619,7 +619,7 @@ mfex_avx512_process(struct dp_packet_batch *packets,
         };
 
         /* This packet has its miniflow created, add to hitmask. */
-        hitmask |= 1 << i;
+        hitmask |= UINT32_C(1) << i;
     }
 
     return hitmask;
diff --git a/lib/dpif-netdev-private-flow.h b/lib/dpif-netdev-private-flow.h
index 66016eb099..7425dd44e7 100644
--- a/lib/dpif-netdev-private-flow.h
+++ b/lib/dpif-netdev-private-flow.h
@@ -104,6 +104,7 @@ struct dp_netdev_flow {
     bool dead;
     uint32_t mark;               /* Unique flow mark for netdev offloading. */
     uint64_t simple_match_mark;  /* Unique flow mark for the simple match. */
+    odp_port_t orig_in_port;
 
     /* Statistics. */
     struct dp_netdev_flow_stats stats;
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 9f35713ef5..51c276b1d8 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1932,13 +1932,13 @@ static void
 dp_netdev_free(struct dp_netdev *dp)
     OVS_REQUIRES(dp_netdev_mutex)
 {
-    struct dp_netdev_port *port, *next;
+    struct dp_netdev_port *port;
     struct tx_bond *bond;
 
     shash_find_and_delete(&dp_netdevs, dp->name);
 
     ovs_rwlock_wrlock(&dp->port_rwlock);
-    HMAP_FOR_EACH_SAFE (port, next, node, &dp->ports) {
+    HMAP_FOR_EACH_SAFE (port, node, &dp->ports) {
         do_del_port(dp, port);
     }
     ovs_rwlock_unlock(&dp->port_rwlock);
@@ -3006,7 +3006,7 @@ static void
 queue_netdev_flow_put(struct dp_netdev_pmd_thread *pmd,
                       struct dp_netdev_flow *flow, struct match *match,
                       const struct nlattr *actions, size_t actions_len,
-                      odp_port_t orig_in_port, int op)
+                      int op)
 {
     struct dp_offload_thread_item *item;
     struct dp_offload_flow_item *flow_offload;
@@ -3021,7 +3021,7 @@ queue_netdev_flow_put(struct dp_netdev_pmd_thread *pmd,
     flow_offload->actions = xmalloc(actions_len);
     memcpy(flow_offload->actions, actions, actions_len);
     flow_offload->actions_len = actions_len;
-    flow_offload->orig_in_port = orig_in_port;
+    flow_offload->orig_in_port = flow->orig_in_port;
 
     item->timestamp = pmd->ctx.now;
     dp_netdev_offload_flow_enqueue(item);
@@ -4095,6 +4095,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
     flow->dead = false;
     flow->batch = NULL;
     flow->mark = INVALID_FLOW_MARK;
+    flow->orig_in_port = orig_in_port;
     *CONST_CAST(unsigned *, &flow->pmd_id) = pmd->core_id;
     *CONST_CAST(struct flow *, &flow->flow) = match->flow;
     *CONST_CAST(ovs_u128 *, &flow->ufid) = *ufid;
@@ -4129,7 +4130,7 @@ dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd,
     }
 
     queue_netdev_flow_put(pmd, flow, match, actions, actions_len,
-                          orig_in_port, DP_NETDEV_FLOW_OFFLOAD_OP_ADD);
+                          DP_NETDEV_FLOW_OFFLOAD_OP_ADD);
     log_netdev_flow_change(flow, match, NULL, actions, actions_len);
 
     return flow;
@@ -4171,7 +4172,7 @@ flow_put_on_pmd(struct dp_netdev_pmd_thread *pmd,
             ovsrcu_set(&netdev_flow->actions, new_actions);
 
             queue_netdev_flow_put(pmd, netdev_flow, match,
-                                  put->actions, put->actions_len, ODPP_NONE,
+                                  put->actions, put->actions_len,
                                   DP_NETDEV_FLOW_OFFLOAD_OP_MOD);
             log_netdev_flow_change(netdev_flow, match, old_actions,
                                    put->actions, put->actions_len);
@@ -5684,23 +5685,28 @@ sched_numa_list_put_in_place(struct sched_numa_list *numa_list)
     }
 }
 
+/* Returns 'true' if OVS rxq scheduling algorithm assigned any unpinned rxq to
+ * a PMD thread core on a non-local numa node. */
 static bool
 sched_numa_list_cross_numa_polling(struct sched_numa_list *numa_list)
 {
     struct sched_numa *numa;
 
-    /* For each numa */
     HMAP_FOR_EACH (numa, node, &numa_list->numas) {
-        /* For each pmd */
         for (int i = 0; i < numa->n_pmds; i++) {
             struct sched_pmd *sched_pmd;
 
             sched_pmd = &numa->pmds[i];
-            /* For each rxq. */
+            if (sched_pmd->isolated) {
+                /* All rxqs on this PMD thread core are pinned. */
+                continue;
+            }
             for (unsigned k = 0; k < sched_pmd->n_rxq; k++) {
                 struct dp_netdev_rxq *rxq = sched_pmd->rxqs[k];
-
-                if (!sched_pmd->isolated &&
+                /* Check if the rxq is not pinned to a specific PMD thread core
+                 * by the user AND the PMD thread core that OVS assigned is
+                 * non-local to the rxq port. */
+                if (rxq->core_id == OVS_CORE_UNSPEC &&
                     rxq->pmd->numa_id !=
                         netdev_get_numa_id(rxq->port->netdev)) {
                     return true;
@@ -6000,10 +6006,10 @@ sched_numa_list_schedule(struct sched_numa_list *numa_list,
             /* Find any numa with available PMDs. */
             for (int j = 0; j < n_numa; j++) {
                 numa = sched_numa_list_next(numa_list, last_cross_numa);
+                last_cross_numa = numa;
                 if (sched_numa_noniso_pmd_count(numa)) {
                     break;
                 }
-                last_cross_numa = numa;
                 numa = NULL;
             }
         }
@@ -6111,7 +6117,7 @@ sched_numa_list_variance(struct sched_numa_list *numa_list)
  * pmd_rebalance_dry_run() can be avoided when it is not needed.
  */
 static bool
-pmd_reblance_dry_run_needed(struct dp_netdev *dp)
+pmd_rebalance_dry_run_needed(struct dp_netdev *dp)
     OVS_REQ_RDLOCK(dp->port_rwlock)
 {
     struct dp_netdev_pmd_thread *pmd;
@@ -6342,11 +6348,11 @@ pmd_remove_stale_ports(struct dp_netdev *dp,
     OVS_EXCLUDED(pmd->port_mutex)
     OVS_REQ_RDLOCK(dp->port_rwlock)
 {
-    struct rxq_poll *poll, *poll_next;
-    struct tx_port *tx, *tx_next;
+    struct rxq_poll *poll;
+    struct tx_port *tx;
 
     ovs_mutex_lock(&pmd->port_mutex);
-    HMAP_FOR_EACH_SAFE (poll, poll_next, node, &pmd->poll_list) {
+    HMAP_FOR_EACH_SAFE (poll, node, &pmd->poll_list) {
         struct dp_netdev_port *port = poll->rxq->port;
 
         if (port->need_reconfigure
@@ -6354,7 +6360,7 @@ pmd_remove_stale_ports(struct dp_netdev *dp,
             dp_netdev_del_rxq_from_pmd(pmd, poll);
         }
     }
-    HMAP_FOR_EACH_SAFE (tx, tx_next, node, &pmd->tx_ports) {
+    HMAP_FOR_EACH_SAFE (tx, node, &pmd->tx_ports) {
         struct dp_netdev_port *port = tx->port;
 
         if (port->need_reconfigure
@@ -6430,8 +6436,7 @@ reconfigure_datapath(struct dp_netdev *dp)
     /* We only reconfigure the ports that we determined above, because they're
      * not being used by any pmd thread at the moment.  If a port fails to
      * reconfigure we remove it from the datapath. */
-    struct dp_netdev_port *next_port;
-    HMAP_FOR_EACH_SAFE (port, next_port, node, &dp->ports) {
+    HMAP_FOR_EACH_SAFE (port, node, &dp->ports) {
         int err;
 
         if (!port->need_reconfigure) {
@@ -6487,10 +6492,10 @@ reconfigure_datapath(struct dp_netdev *dp)
     }
 
     CMAP_FOR_EACH (pmd, node, &dp->poll_threads) {
-        struct rxq_poll *poll, *poll_next;
+        struct rxq_poll *poll;
 
         ovs_mutex_lock(&pmd->port_mutex);
-        HMAP_FOR_EACH_SAFE (poll, poll_next, node, &pmd->poll_list) {
+        HMAP_FOR_EACH_SAFE (poll, node, &pmd->poll_list) {
             if (poll->rxq->pmd != pmd) {
                 dp_netdev_del_rxq_from_pmd(pmd, poll);
 
@@ -6682,7 +6687,7 @@ dpif_netdev_run(struct dpif *dpif)
             if (pmd_rebalance &&
                 !dp_netdev_is_reconf_required(dp) &&
                 !ports_require_restart(dp) &&
-                pmd_reblance_dry_run_needed(dp) &&
+                pmd_rebalance_dry_run_needed(dp) &&
                 pmd_rebalance_dry_run(dp)) {
                 VLOG_INFO("PMD auto load balance dry run. "
                           "Requesting datapath reconfigure.");
@@ -7364,15 +7369,15 @@ static struct dp_netdev_pmd_thread *
 dp_netdev_get_pmd(struct dp_netdev *dp, unsigned core_id)
 {
     struct dp_netdev_pmd_thread *pmd;
-    const struct cmap_node *pnode;
 
-    pnode = cmap_find(&dp->poll_threads, hash_int(core_id, 0));
-    if (!pnode) {
-        return NULL;
+    CMAP_FOR_EACH_WITH_HASH (pmd, node, hash_int(core_id, 0),
+                             &dp->poll_threads) {
+        if (pmd->core_id == core_id) {
+            return dp_netdev_pmd_try_ref(pmd) ? pmd : NULL;
+        }
     }
-    pmd = CONTAINER_OF(pnode, struct dp_netdev_pmd_thread, node);
 
-    return dp_netdev_pmd_try_ref(pmd) ? pmd : NULL;
+    return NULL;
 }
 
 /* Sets the 'struct dp_netdev_pmd_thread' for non-pmd threads. */
diff --git a/lib/fat-rwlock.c b/lib/fat-rwlock.c
index d913b2088f..771ccc9737 100644
--- a/lib/fat-rwlock.c
+++ b/lib/fat-rwlock.c
@@ -97,14 +97,14 @@ fat_rwlock_init(struct fat_rwlock *rwlock)
 void
 fat_rwlock_destroy(struct fat_rwlock *rwlock)
 {
-    struct fat_rwlock_slot *slot, *next;
+    struct fat_rwlock_slot *slot;
 
     /* Order is important here.  By destroying the thread-specific data first,
      * before we destroy the slots, we ensure that the thread-specific
      * data destructor can't race with our loop below. */
     ovsthread_key_delete(rwlock->key);
 
-    LIST_FOR_EACH_SAFE (slot, next, list_node, &rwlock->threads) {
+    LIST_FOR_EACH_SAFE (slot, list_node, &rwlock->threads) {
         free_slot(slot);
     }
     ovs_mutex_destroy(&rwlock->mutex);
diff --git a/lib/hindex.h b/lib/hindex.h
index 876c5a9e39..ea7402587e 100644
--- a/lib/hindex.h
+++ b/lib/hindex.h
@@ -128,18 +128,38 @@ void hindex_remove(struct hindex *, struct hindex_node *);
  * Evaluates HASH only once.
  */
 #define HINDEX_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, HINDEX)               \
-    for (INIT_CONTAINER(NODE, hindex_node_with_hash(HINDEX, HASH), MEMBER); \
-         NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER);                     \
-         ASSIGN_CONTAINER(NODE, (NODE)->MEMBER.s, MEMBER))
+    for (INIT_MULTIVAR(NODE, MEMBER, hindex_node_with_hash(HINDEX, HASH),   \
+                       struct hindex_node);                                 \
+         CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL);          \
+         UPDATE_MULTIVAR(NODE, ITER_VAR(NODE)->s))
 
 /* Safe when NODE may be freed (not needed when NODE may be removed from the
  * hash map but its members remain accessible and intact). */
-#define HINDEX_FOR_EACH_WITH_HASH_SAFE(NODE, NEXT, MEMBER, HASH, HINDEX) \
-    for (INIT_CONTAINER(NODE, hindex_node_with_hash(HINDEX, HASH), MEMBER); \
-         (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)                 \
-          ? INIT_CONTAINER(NEXT, (NODE)->MEMBER.s, MEMBER), 1           \
-          : 0);                                                         \
-         (NODE) = (NEXT))
+#define HINDEX_FOR_EACH_WITH_HASH_SAFE_LONG(NODE, NEXT, MEMBER, HASH, HINDEX) \
+    for (INIT_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER,                          \
+                                hindex_node_with_hash(HINDEX, HASH),          \
+                                struct hindex_node);                          \
+         CONDITION_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER,                     \
+                                      ITER_VAR(NODE) != NULL,                 \
+                                      ITER_VAR(NEXT) = ITER_VAR(NODE)->s,     \
+                                      ITER_VAR(NEXT) != NULL);                \
+         UPDATE_MULTIVAR_SAFE_LONG(NODE, NEXT))
+
+/* Short version of HINDEX_FOR_EACH_WITH_HASH_SAFE. */
+#define HINDEX_FOR_EACH_WITH_HASH_SAFE_SHORT(NODE, MEMBER, HASH, HINDEX)      \
+    for (INIT_MULTIVAR_SAFE_SHORT(NODE, MEMBER,                               \
+                            hindex_node_with_hash(HINDEX, HASH),              \
+                            struct hindex_node);                              \
+         CONDITION_MULTIVAR_SAFE_SHORT(NODE, MEMBER,                          \
+                                       ITER_VAR(NODE) != NULL,                \
+                                 ITER_NEXT_VAR(NODE) = ITER_VAR(NODE)->s);    \
+         UPDATE_MULTIVAR_SAFE_SHORT(NODE))
+
+#define HINDEX_FOR_EACH_WITH_HASH_SAFE(...)                                   \
+    OVERLOAD_SAFE_MACRO(HINDEX_FOR_EACH_WITH_HASH_SAFE_LONG,                  \
+                        HINDEX_FOR_EACH_WITH_HASH_SAFE_SHORT,                 \
+                        5, __VA_ARGS__)
+
 
 /* Returns the head node in 'hindex' with the given 'hash', or a null pointer
  * if no nodes have that hash value. */
@@ -157,19 +177,36 @@ hindex_node_with_hash(const struct hindex *hindex, size_t hash)
 /* Iteration. */
 
 /* Iterates through every node in HINDEX. */
-#define HINDEX_FOR_EACH(NODE, MEMBER, HINDEX)                           \
-    for (INIT_CONTAINER(NODE, hindex_first(HINDEX), MEMBER);            \
-         NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER);                 \
-         ASSIGN_CONTAINER(NODE, hindex_next(HINDEX, &(NODE)->MEMBER), MEMBER))
+#define HINDEX_FOR_EACH(NODE, MEMBER, HINDEX)                                 \
+    for (INIT_MULTIVAR(NODE, MEMBER, hindex_first(HINDEX),                    \
+                       struct hindex_node);                                   \
+         CONDITION_MULTIVAR(NODE, MEMBER, ITER_VAR(NODE) != NULL);            \
+         UPDATE_MULTIVAR(NODE, hindex_next(HINDEX, ITER_VAR(NODE))))
 
 /* Safe when NODE may be freed (not needed when NODE may be removed from the
  * hash index but its members remain accessible and intact). */
-#define HINDEX_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HINDEX)              \
-    for (INIT_CONTAINER(NODE, hindex_first(HINDEX), MEMBER);          \
-         (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)                 \
-          ? INIT_CONTAINER(NEXT, hindex_next(HINDEX, &(NODE)->MEMBER), MEMBER), 1 \
-          : 0);                                                         \
-         (NODE) = (NEXT))
+#define HINDEX_FOR_EACH_SAFE_LONG(NODE, NEXT, MEMBER, HINDEX)                 \
+    for (INIT_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER, hindex_first(HINDEX),    \
+                                 struct hindex_node);                         \
+         CONDITION_MULTIVAR_SAFE_LONG(NODE, NEXT, MEMBER,                     \
+                                      ITER_VAR(NODE) != NULL,                 \
+                        ITER_VAR(NEXT) = hindex_next(HINDEX, ITER_VAR(NODE)), \
+                                      ITER_VAR(NEXT) != NULL);                \
+         UPDATE_MULTIVAR_SAFE_LONG(NODE, NEXT))
+
+/* Short version of HINDEX_FOR_EACH_SAFE. */
+#define HINDEX_FOR_EACH_SAFE_SHORT(NODE, MEMBER, HINDEX)                      \
+    for (INIT_MULTIVAR_SAFE_SHORT(NODE, MEMBER, hindex_first(HINDEX),         \
+                                  struct hindex_node);                        \
+         CONDITION_MULTIVAR_SAFE_SHORT(NODE, MEMBER,                          \
+                                       ITER_VAR(NODE) != NULL,                \
+              ITER_NEXT_VAR(NODE) = hindex_next(HINDEX, ITER_VAR(NODE)));     \
+         UPDATE_MULTIVAR_SAFE_SHORT(NODE))
+
+#define HINDEX_FOR_EACH_SAFE(...)                                             \
+    OVERLOAD_SAFE_MACRO(HINDEX_FOR_EACH_SAFE_LONG,                            \
+                        HINDEX_FOR_EACH_SAFE_SHORT,                           \
+                        4, __VA_ARGS__)
 
 struct hindex_node *hindex_first(const struct hindex *);
 struct hindex_node *hindex_next(const struct hindex *,
diff --git a/lib/hmapx.c b/lib/hmapx.c
index eadfe640ac..68192fc2c5 100644
--- a/lib/hmapx.c
+++ b/lib/hmapx.c
@@ -123,9 +123,9 @@ hmapx_add_assert(struct hmapx *map, void *data)
 void
 hmapx_clear(struct hmapx *map)
 {
-    struct hmapx_node *node, *next;
+    struct hmapx_node *node;
 
-    HMAPX_FOR_EACH_SAFE (node, next, map) {
+    HMAPX_FOR_EACH_SAFE (node, map) {
         hmapx_delete(map, node);
     }
 }
diff --git a/lib/hmapx.h b/lib/hmapx.h
index 06a6bbe67d..0b03411491 100644
--- a/lib/hmapx.h
+++ b/lib/hmapx.h
@@ -67,10 +67,20 @@ bool hmapx_equals(const struct hmapx *, const struct hmapx *);
 
 /* Safe when NODE may be freed (not needed when NODE may be removed from the
  * hash map but its members remain accessible and intact). */
-#define HMAPX_FOR_EACH_SAFE(NODE, NEXT, HMAPX)                          \
-    HMAP_FOR_EACH_SAFE_INIT(NODE, NEXT, hmap_node, &(HMAPX)->map,       \
+#define HMAPX_FOR_EACH_SAFE_SHORT(NODE, HMAPX)                            \
+    HMAP_FOR_EACH_SAFE_SHORT_INIT (NODE, hmap_node, &(HMAPX)->map,        \
+                            BUILD_ASSERT_TYPE(NODE, struct hmapx_node *), \
+                            BUILD_ASSERT_TYPE(HMAPX, struct hmapx *))
+
+#define HMAPX_FOR_EACH_SAFE_LONG(NODE, NEXT, HMAPX)                       \
+    HMAP_FOR_EACH_SAFE_LONG_INIT (NODE, NEXT, hmap_node, &(HMAPX)->map,   \
                             BUILD_ASSERT_TYPE(NODE, struct hmapx_node *), \
                             BUILD_ASSERT_TYPE(NEXT, struct hmapx_node *), \
                             BUILD_ASSERT_TYPE(HMAPX, struct hmapx *))
 
+#define HMAPX_FOR_EACH_SAFE(...)                                              \
+    OVERLOAD_SAFE_MACRO(HMAPX_FOR_EACH_SAFE_LONG,                             \
+                        HMAPX_FOR_EACH_SAFE_SHORT,                            \
+                        3, __VA_ARGS__)
+
 #endif /* hmapx.h */
diff --git a/lib/id-fpool.c b/lib/id-fpool.c
index 15cef5d003..7108c104a4 100644
--- a/lib/id-fpool.c
+++ b/lib/id-fpool.c
@@ -166,11 +166,10 @@ void
 id_fpool_destroy(struct id_fpool *pool)
 {
     struct id_slab *slab;
-    struct id_slab *next;
     size_t i;
 
     id_fpool_lock(&pool->pool_lock);
-    LIST_FOR_EACH_SAFE (slab, next, node, &pool->free_slabs) {
+    LIST_FOR_EACH_SAFE (slab, node, &pool->free_slabs) {
         free(slab);
     }
     ovs_list_poison(&pool->free_slabs);
diff --git a/lib/ipf.c b/lib/ipf.c
index 507db2aea2..d452663743 100644
--- a/lib/ipf.c
+++ b/lib/ipf.c
@@ -1058,9 +1058,9 @@ ipf_send_completed_frags(struct ipf *ipf, struct dp_packet_batch *pb,
     }
 
     ovs_mutex_lock(&ipf->ipf_lock);
-    struct ipf_list *ipf_list, *next;
+    struct ipf_list *ipf_list;
 
-    LIST_FOR_EACH_SAFE (ipf_list, next, list_node, &ipf->frag_complete_list) {
+    LIST_FOR_EACH_SAFE (ipf_list, list_node, &ipf->frag_complete_list) {
         if (ipf_send_frags_in_list(ipf, ipf_list, pb, IPF_FRAG_COMPLETED_LIST,
                                    v6, now)) {
             ipf_completed_list_clean(&ipf->frag_lists, ipf_list);
@@ -1090,10 +1090,10 @@ ipf_send_expired_frags(struct ipf *ipf, struct dp_packet_batch *pb,
     }
 
     ovs_mutex_lock(&ipf->ipf_lock);
-    struct ipf_list *ipf_list, *next;
+    struct ipf_list *ipf_list;
     size_t lists_removed = 0;
 
-    LIST_FOR_EACH_SAFE (ipf_list, next, list_node, &ipf->frag_exp_list) {
+    LIST_FOR_EACH_SAFE (ipf_list, list_node, &ipf->frag_exp_list) {
         if (now <= ipf_list->expiration ||
             lists_removed >= IPF_FRAG_LIST_MAX_EXPIRED) {
             break;
@@ -1121,9 +1121,9 @@ ipf_execute_reass_pkts(struct ipf *ipf, struct dp_packet_batch *pb)
     }
 
     ovs_mutex_lock(&ipf->ipf_lock);
-    struct reassembled_pkt *rp, *next;
+    struct reassembled_pkt *rp;
 
-    LIST_FOR_EACH_SAFE (rp, next, rp_list_node, &ipf->reassembled_pkt_list) {
+    LIST_FOR_EACH_SAFE (rp, rp_list_node, &ipf->reassembled_pkt_list) {
         if (!rp->list->reass_execute_ctx &&
             ipf_dp_packet_batch_add(pb, rp->pkt, false)) {
             rp->list->reass_execute_ctx = rp->pkt;
@@ -1144,9 +1144,9 @@ ipf_post_execute_reass_pkts(struct ipf *ipf,
     }
 
     ovs_mutex_lock(&ipf->ipf_lock);
-    struct reassembled_pkt *rp, *next;
+    struct reassembled_pkt *rp;
 
-    LIST_FOR_EACH_SAFE (rp, next, rp_list_node, &ipf->reassembled_pkt_list) {
+    LIST_FOR_EACH_SAFE (rp, rp_list_node, &ipf->reassembled_pkt_list) {
         const size_t pb_cnt = dp_packet_batch_size(pb);
         int pb_idx;
         struct dp_packet *pkt;
@@ -1271,15 +1271,15 @@ ipf_clean_thread_main(void *f)
 
             ovs_mutex_lock(&ipf->ipf_lock);
 
-            struct ipf_list *ipf_list, *next;
-            LIST_FOR_EACH_SAFE (ipf_list, next, list_node,
+            struct ipf_list *ipf_list;
+            LIST_FOR_EACH_SAFE (ipf_list, list_node,
                                 &ipf->frag_exp_list) {
                 if (ipf_purge_list_check(ipf, ipf_list, now)) {
                     ipf_expiry_list_clean(&ipf->frag_lists, ipf_list);
                 }
             }
 
-            LIST_FOR_EACH_SAFE (ipf_list, next, list_node,
+            LIST_FOR_EACH_SAFE (ipf_list, list_node,
                                 &ipf->frag_complete_list) {
                 if (ipf_purge_list_check(ipf, ipf_list, now)) {
                     ipf_completed_list_clean(&ipf->frag_lists, ipf_list);
diff --git a/lib/json.c b/lib/json.c
index 720c73d940..042aab83b3 100644
--- a/lib/json.c
+++ b/lib/json.c
@@ -397,9 +397,9 @@ json_destroy__(struct json *json)
 static void
 json_destroy_object(struct shash *object)
 {
-    struct shash_node *node, *next;
+    struct shash_node *node;
 
-    SHASH_FOR_EACH_SAFE (node, next, object) {
+    SHASH_FOR_EACH_SAFE (node, object) {
         struct json *value = node->data;
 
         json_destroy(value);
diff --git a/lib/lacp.c b/lib/lacp.c
index 89d711225f..3252f17ebf 100644
--- a/lib/lacp.c
+++ b/lib/lacp.c
@@ -280,10 +280,10 @@ void
 lacp_unref(struct lacp *lacp) OVS_EXCLUDED(mutex)
 {
     if (lacp && ovs_refcount_unref_relaxed(&lacp->ref_cnt) == 1) {
-        struct member *member, *next;
+        struct member *member;
 
         lacp_lock();
-        HMAP_FOR_EACH_SAFE (member, next, node, &lacp->members) {
+        HMAP_FOR_EACH_SAFE (member, node, &lacp->members) {
             member_destroy(member);
         }
 
diff --git a/lib/lldp/lldpd-structs.c b/lib/lldp/lldpd-structs.c
index 499b441746..a8c7fad098 100644
--- a/lib/lldp/lldpd-structs.c
+++ b/lib/lldp/lldpd-structs.c
@@ -64,11 +64,11 @@ lldpd_remote_cleanup(struct lldpd_hardware *hw,
                                    struct lldpd_port *),
                      bool all)
 {
-    struct lldpd_port *port, *port_next;
+    struct lldpd_port *port;
     time_t now = time_now();
 
     VLOG_DBG("cleanup remote port on %s", hw->h_ifname);
-    LIST_FOR_EACH_SAFE (port, port_next, p_entries, &hw->h_rports) {
+    LIST_FOR_EACH_SAFE (port, p_entries, &hw->h_rports) {
         bool del = all;
         if (!all && expire &&
             (now >= port->p_lastupdate + port->p_chassis->c_ttl)) {
@@ -99,11 +99,10 @@ static void
 lldpd_aa_maps_cleanup(struct lldpd_port *port)
 {
     struct lldpd_aa_isid_vlan_maps_tlv *isid_vlan_map = NULL;
-    struct lldpd_aa_isid_vlan_maps_tlv *isid_vlan_map_next = NULL;
 
     if (!ovs_list_is_empty(&port->p_isid_vlan_maps)) {
 
-        LIST_FOR_EACH_SAFE (isid_vlan_map, isid_vlan_map_next, m_entries,
+        LIST_FOR_EACH_SAFE (isid_vlan_map, m_entries,
                             &port->p_isid_vlan_maps) {
 
             ovs_list_remove(&isid_vlan_map->m_entries);
diff --git a/lib/lldp/lldpd.c b/lib/lldp/lldpd.c
index a024dc5e58..403f1f525a 100644
--- a/lib/lldp/lldpd.c
+++ b/lib/lldp/lldpd.c
@@ -134,12 +134,12 @@ lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
 void
 lldpd_cleanup(struct lldpd *cfg)
 {
-    struct lldpd_hardware *hw, *hw_next;
-    struct lldpd_chassis *chassis, *chassis_next;
+    struct lldpd_hardware *hw;
+    struct lldpd_chassis *chassis;
 
     VLOG_DBG("cleanup all ports");
 
-    LIST_FOR_EACH_SAFE (hw, hw_next, h_entries, &cfg->g_hardware) {
+    LIST_FOR_EACH_SAFE (hw, h_entries, &cfg->g_hardware) {
         if (!hw->h_flags) {
             ovs_list_remove(&hw->h_entries);
             lldpd_remote_cleanup(hw, NULL, true);
@@ -151,7 +151,7 @@ lldpd_cleanup(struct lldpd *cfg)
 
     VLOG_DBG("cleanup all chassis");
 
-    LIST_FOR_EACH_SAFE (chassis, chassis_next, list, &cfg->g_chassis) {
+    LIST_FOR_EACH_SAFE (chassis, list, &cfg->g_chassis) {
         if (chassis->c_refcount == 0) {
             ovs_list_remove(&chassis->list);
             lldpd_chassis_cleanup(chassis, 1);
diff --git a/lib/mac-learning.c b/lib/mac-learning.c
index 3fcd7d9b77..a60794fb26 100644
--- a/lib/mac-learning.c
+++ b/lib/mac-learning.c
@@ -244,10 +244,10 @@ void
 mac_learning_unref(struct mac_learning *ml)
 {
     if (ml && ovs_refcount_unref(&ml->ref_cnt) == 1) {
-        struct mac_entry *e, *next;
+        struct mac_entry *e;
 
         ovs_rwlock_wrlock(&ml->rwlock);
-        HMAP_FOR_EACH_SAFE (e, next, hmap_node, &ml->table) {
+        HMAP_FOR_EACH_SAFE (e, hmap_node, &ml->table) {
             mac_learning_expire(ml, e);
         }
         hmap_destroy(&ml->table);
diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c
index 6730301b67..029ca28558 100644
--- a/lib/mcast-snooping.c
+++ b/lib/mcast-snooping.c
@@ -356,11 +356,11 @@ mcast_snooping_prune_expired(struct mcast_snooping *ms,
     OVS_REQ_WRLOCK(ms->rwlock)
 {
     int expired;
-    struct mcast_group_bundle *b, *next_b;
+    struct mcast_group_bundle *b;
     time_t timenow = time_now();
 
     expired = 0;
-    LIST_FOR_EACH_SAFE (b, next_b, bundle_node, &grp->bundle_lru) {
+    LIST_FOR_EACH_SAFE (b, bundle_node, &grp->bundle_lru) {
         /* This list is sorted on expiration time. */
         if (b->expires > timenow) {
             break;
@@ -946,15 +946,15 @@ mcast_snooping_wait(struct mcast_snooping *ms)
 void
 mcast_snooping_flush_bundle(struct mcast_snooping *ms, void *port)
 {
-    struct mcast_group *g, *next_g;
-    struct mcast_mrouter_bundle *m, *next_m;
+    struct mcast_group *g;
+    struct mcast_mrouter_bundle *m;
 
     if (!mcast_snooping_enabled(ms)) {
         return;
     }
 
     ovs_rwlock_wrlock(&ms->rwlock);
-    LIST_FOR_EACH_SAFE (g, next_g, group_node, &ms->group_lru) {
+    LIST_FOR_EACH_SAFE (g, group_node, &ms->group_lru) {
         if (mcast_group_delete_bundle(ms, g, port)) {
             ms->need_revalidate = true;
 
@@ -964,7 +964,7 @@ mcast_snooping_flush_bundle(struct mcast_snooping *ms, void *port)
         }
     }
 
-    LIST_FOR_EACH_SAFE (m, next_m, mrouter_node, &ms->mrouter_lru) {
+    LIST_FOR_EACH_SAFE (m, mrouter_node, &ms->mrouter_lru) {
         if (m->port == port) {
             mcast_snooping_flush_mrouter(m);
             ms->need_revalidate = true;
diff --git a/lib/namemap.c b/lib/namemap.c
index 785cda4c27..dd317ea52e 100644
--- a/lib/namemap.c
+++ b/lib/namemap.c
@@ -90,9 +90,9 @@ void
 namemap_destroy(struct namemap *map)
 {
     if (map) {
-        struct namemap_node *node, *next;
+        struct namemap_node *node;
 
-        HMAP_FOR_EACH_SAFE (node, next, name_node, &map->by_name) {
+        HMAP_FOR_EACH_SAFE (node, name_node, &map->by_name) {
             hmap_remove(&map->by_name, &node->name_node);
             hmap_remove(&map->by_number, &node->number_node);
             free(node->name);
diff --git a/lib/netdev-afxdp.c b/lib/netdev-afxdp.c
index 482400d8d1..ca3f2431ea 100644
--- a/lib/netdev-afxdp.c
+++ b/lib/netdev-afxdp.c
@@ -235,11 +235,11 @@ netdev_afxdp_cleanup_unused_pool(struct unused_pool *pool)
 static void
 netdev_afxdp_sweep_unused_pools(void *aux OVS_UNUSED)
 {
-    struct unused_pool *pool, *next;
+    struct unused_pool *pool;
     unsigned int count;
 
     ovs_mutex_lock(&unused_pools_mutex);
-    LIST_FOR_EACH_SAFE (pool, next, list_node, &unused_pools) {
+    LIST_FOR_EACH_SAFE (pool, list_node, &unused_pools) {
 
         count = umem_pool_count(&pool->umem_info->mpool);
         ovs_assert(count + pool->lost_in_rings <= NUM_FRAMES);
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index b6b29c75e3..4d32781a95 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -622,9 +622,9 @@ dpdk_mp_full(const struct rte_mempool *mp) OVS_REQUIRES(dpdk_mp_mutex)
 static void
 dpdk_mp_sweep(void) OVS_REQUIRES(dpdk_mp_mutex)
 {
-    struct dpdk_mp *dmp, *next;
+    struct dpdk_mp *dmp;
 
-    LIST_FOR_EACH_SAFE (dmp, next, list_node, &dpdk_mp_list) {
+    LIST_FOR_EACH_SAFE (dmp, list_node, &dpdk_mp_list) {
         if (!dmp->refcount && dpdk_mp_full(dmp->mp)) {
             VLOG_DBG("Freeing mempool \"%s\"", dmp->mp->name);
             ovs_list_remove(&dmp->list_node);
@@ -4686,11 +4686,11 @@ trtcm_policer_qos_construct(const struct smap *details,
 static void
 trtcm_policer_qos_destruct(struct qos_conf *conf)
 {
-    struct trtcm_policer_queue *queue, *next_queue;
+    struct trtcm_policer_queue *queue;
     struct trtcm_policer *policer = CONTAINER_OF(conf, struct trtcm_policer,
                                                  qos_conf);
 
-    HMAP_FOR_EACH_SAFE (queue, next_queue, hmap_node, &policer->queues) {
+    HMAP_FOR_EACH_SAFE (queue, hmap_node, &policer->queues) {
         hmap_remove(&policer->queues, &queue->hmap_node);
         free(queue);
     }
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 620a451dec..9d125029de 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -5331,11 +5331,11 @@ static void
 hfsc_tc_destroy(struct tc *tc)
 {
     struct hfsc *hfsc;
-    struct hfsc_class *hc, *next;
+    struct hfsc_class *hc;
 
     hfsc = CONTAINER_OF(tc, struct hfsc, tc);
 
-    HMAP_FOR_EACH_SAFE (hc, next, tc_queue.hmap_node, &hfsc->tc.queues) {
+    HMAP_FOR_EACH_SAFE (hc, tc_queue.hmap_node, &hfsc->tc.queues) {
         hmap_remove(&hfsc->tc.queues, &hc->tc_queue.hmap_node);
         free(hc);
     }
diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index 9845e8d3fe..a41b627585 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -417,11 +417,11 @@ delete_chains_from_netdev(struct netdev *netdev, struct tcf_id *id)
 static int
 netdev_tc_flow_flush(struct netdev *netdev)
 {
-    struct ufid_tc_data *data, *next;
+    struct ufid_tc_data *data;
     int err;
 
     ovs_mutex_lock(&ufid_lock);
-    HMAP_FOR_EACH_SAFE (data, next, tc_to_ufid_node, &tc_to_ufid) {
+    HMAP_FOR_EACH_SAFE (data, tc_to_ufid_node, &tc_to_ufid) {
         if (data->netdev != netdev) {
             continue;
         }
@@ -481,10 +481,10 @@ netdev_tc_flow_dump_destroy(struct netdev_flow_dump *dump)
 
 static void
 parse_flower_rewrite_to_netlink_action(struct ofpbuf *buf,
-                                       struct tc_flower *flower)
+                                       struct tc_action *action)
 {
-    char *mask = (char *) &flower->rewrite.mask;
-    char *data = (char *) &flower->rewrite.key;
+    char *mask = (char *) &action->rewrite.mask;
+    char *data = (char *) &action->rewrite.key;
 
     for (int type = 0; type < ARRAY_SIZE(set_flower_map); type++) {
         char *put = NULL;
@@ -585,8 +585,10 @@ parse_tc_flower_to_stats(struct tc_flower *flower,
     }
 
     memset(stats, 0, sizeof *stats);
-    stats->n_packets = get_32aligned_u64(&flower->stats.n_packets);
-    stats->n_bytes = get_32aligned_u64(&flower->stats.n_bytes);
+    stats->n_packets = get_32aligned_u64(&flower->stats_sw.n_packets);
+    stats->n_packets += get_32aligned_u64(&flower->stats_hw.n_packets);
+    stats->n_bytes = get_32aligned_u64(&flower->stats_sw.n_bytes);
+    stats->n_bytes += get_32aligned_u64(&flower->stats_hw.n_bytes);
     stats->used = flower->lastused;
 }
 
@@ -877,7 +879,7 @@ parse_tc_flower_to_match(struct tc_flower *flower,
             }
             break;
             case TC_ACT_PEDIT: {
-                parse_flower_rewrite_to_netlink_action(buf, flower);
+                parse_flower_rewrite_to_netlink_action(buf, action);
             }
             break;
             case TC_ACT_ENCAP: {
@@ -1222,8 +1224,8 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
     uint64_t set_stub[1024 / 8];
     struct ofpbuf set_buf = OFPBUF_STUB_INITIALIZER(set_stub);
     char *set_data, *set_mask;
-    char *key = (char *) &flower->rewrite.key;
-    char *mask = (char *) &flower->rewrite.mask;
+    char *key = (char *) &action->rewrite.key;
+    char *mask = (char *) &action->rewrite.mask;
     const struct nlattr *attr;
     int i, j, type;
     size_t size;
@@ -1265,14 +1267,6 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
         }
     }
 
-    if (!is_all_zeros(&flower->rewrite, sizeof flower->rewrite)) {
-        if (flower->rewrite.rewrite == false) {
-            flower->rewrite.rewrite = true;
-            action->type = TC_ACT_PEDIT;
-            flower->action_count++;
-        }
-    }
-
     if (hasmask && !is_all_zeros(set_mask, size)) {
         VLOG_DBG_RL(&rl, "unsupported sub attribute of set action type %d",
                     type);
@@ -1281,6 +1275,8 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
     }
 
     ofpbuf_uninit(&set_buf);
+    action->type = TC_ACT_PEDIT;
+    flower->action_count++;
     return 0;
 }
 
@@ -1541,6 +1537,12 @@ parse_match_ct_state_to_flower(struct tc_flower *flower, struct match *match)
             flower->key.ct_state &= ~(TCA_FLOWER_KEY_CT_FLAGS_NEW);
             flower->mask.ct_state &= ~(TCA_FLOWER_KEY_CT_FLAGS_NEW);
         }
+
+        if (flower->key.ct_state &&
+            !(flower->key.ct_state & TCA_FLOWER_KEY_CT_FLAGS_TRACKED)) {
+            flower->key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_TRACKED;
+            flower->mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_TRACKED;
+        }
     }
 
     if (mask->ct_zone) {
@@ -1841,7 +1843,25 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
                 VLOG_DBG_RL(&rl, "Can't find netdev for output port %d", port);
                 return ENODEV;
             }
+
+            if (!netdev_flow_api_equals(netdev, outdev)) {
+                VLOG_DBG_RL(&rl,
+                            "Flow API provider mismatch between ingress (%s) "
+                            "and egress (%s) ports",
+                            netdev_get_name(netdev), netdev_get_name(outdev));
+                netdev_close(outdev);
+                return EOPNOTSUPP;
+            }
+
             action->out.ifindex_out = netdev_get_ifindex(outdev);
+            if (action->out.ifindex_out < 0) {
+                VLOG_DBG_RL(&rl,
+                            "Can't find ifindex for output port %s, error %d",
+                            netdev_get_name(outdev), action->out.ifindex_out);
+                netdev_close(outdev);
+                return -action->out.ifindex_out;
+            }
+
             action->out.ingress = is_internal_port(netdev_get_type(outdev));
             action->type = TC_ACT_OUTPUT;
             flower.action_count++;
@@ -2015,9 +2035,7 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
     if (stats) {
         memset(stats, 0, sizeof *stats);
         if (!tc_get_flower(&id, &flower)) {
-            stats->n_packets = get_32aligned_u64(&flower.stats.n_packets);
-            stats->n_bytes = get_32aligned_u64(&flower.stats.n_bytes);
-            stats->used = flower.lastused;
+            parse_tc_flower_to_stats(&flower, stats);
         }
     }
 
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 9a705cffa3..3ae850b669 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -4630,6 +4630,11 @@ odp_flow_format(const struct nlattr *key, size_t key_len,
                     ds_put_char(ds, ',');
                 }
                 ds_put_cstr(ds, "eth()");
+            } else if (attr_type == OVS_KEY_ATTR_PACKET_TYPE && is_wildcard) {
+                /* See the above help text, however in the case where the
+                 * packet type is not shown, we still need to display the
+                 * eth() header if the packets type is wildcarded. */
+                has_packet_type_key = false;
             }
             ofpbuf_clear(&ofp);
         }
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 006837c2e1..a0b70a89d7 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -853,7 +853,9 @@ decode_NXAST_RAW_CONTROLLER2(const struct ext_action_header *eah,
         case NXAC2PT_REASON: {
             uint8_t u8;
             error = ofpprop_parse_u8(&payload, &u8);
-            oc->reason = u8;
+            if (!error) {
+                oc->reason = u8;
+            }
             break;
         }
 
diff --git a/lib/ofp-msgs.c b/lib/ofp-msgs.c
index fec54f75f8..93aa812978 100644
--- a/lib/ofp-msgs.c
+++ b/lib/ofp-msgs.c
@@ -1123,8 +1123,8 @@ ofpmp_partial_error(struct hmap *assembler, struct ofpmp_partial *p,
 void
 ofpmp_assembler_clear(struct hmap *assembler)
 {
-    struct ofpmp_partial *p, *next;
-    HMAP_FOR_EACH_SAFE (p, next, hmap_node, assembler) {
+    struct ofpmp_partial *p;
+    HMAP_FOR_EACH_SAFE (p, hmap_node, assembler) {
         ofpmp_partial_destroy(assembler, p);
     }
 }
@@ -1290,8 +1290,8 @@ ofpmp_assembler_execute(struct hmap *assembler, struct ofpbuf *msg,
      * on either side by parts with 0-byte bodies.  We remove the 0-byte
      * ones here to simplify processing later.
      */
-    struct ofpbuf *b, *next;
-    LIST_FOR_EACH_SAFE (b, next, list_node, out) {
+    struct ofpbuf *b;
+    LIST_FOR_EACH_SAFE (b, list_node, out) {
         if (b->size <= min_len && !ovs_list_is_short(out)) {
             ovs_list_remove(&b->list_node);
             ofpbuf_delete(b);
diff --git a/lib/ofp-packet.c b/lib/ofp-packet.c
index 4579548ee1..9485ddfc93 100644
--- a/lib/ofp-packet.c
+++ b/lib/ofp-packet.c
@@ -133,7 +133,9 @@ decode_nx_packet_in2(const struct ofp_header *oh, bool loose,
         case NXPINT_FULL_LEN: {
             uint32_t u32;
             error = ofpprop_parse_u32(&payload, &u32);
-            *total_len = u32;
+            if (!error) {
+                *total_len = u32;
+            }
             break;
         }
 
@@ -152,7 +154,9 @@ decode_nx_packet_in2(const struct ofp_header *oh, bool loose,
         case NXPINT_REASON: {
             uint8_t reason;
             error = ofpprop_parse_u8(&payload, &reason);
-            pin->reason = reason;
+            if (!error) {
+                pin->reason = reason;
+            }
             break;
         }
 
@@ -883,7 +887,9 @@ ofputil_decode_packet_in_private(const struct ofp_header *oh, bool loose,
         case NXCPT_ODP_PORT: {
             uint32_t value;
             error = ofpprop_parse_u32(&payload, &value);
-            pin->odp_port = u32_to_odp(value);
+            if (!error) {
+                pin->odp_port = u32_to_odp(value);
+            }
             break;
          }
 
diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
index 162311fa45..a9d205ec83 100644
--- a/lib/ovs-lldp.c
+++ b/lib/ovs-lldp.c
@@ -559,9 +559,9 @@ aa_mapping_unregister_mapping(struct lldp *lldp,
                               struct lldpd_hardware *hw,
                               struct aa_mapping_internal *m)
 {
-    struct lldpd_aa_isid_vlan_maps_tlv *lm, *lm_next;
+    struct lldpd_aa_isid_vlan_maps_tlv *lm;
 
-    LIST_FOR_EACH_SAFE (lm, lm_next, m_entries,
+    LIST_FOR_EACH_SAFE (lm, m_entries,
                         &hw->h_lport.p_isid_vlan_maps) {
         uint32_t isid = lm->isid_vlan_data.isid;
 
@@ -953,8 +953,8 @@ lldp_ref(const struct lldp *lldp_)
 void
 lldp_destroy_dummy(struct lldp *lldp)
 {
-    struct lldpd_hardware *hw, *hw_next;
-    struct lldpd_chassis *chassis, *chassis_next;
+    struct lldpd_hardware *hw;
+    struct lldpd_chassis *chassis;
     struct lldpd *cfg;
 
     if (!lldp) {
@@ -963,13 +963,13 @@ lldp_destroy_dummy(struct lldp *lldp)
 
     cfg = lldp->lldpd;
 
-    LIST_FOR_EACH_SAFE (hw, hw_next, h_entries, &cfg->g_hardware) {
+    LIST_FOR_EACH_SAFE (hw, h_entries, &cfg->g_hardware) {
         ovs_list_remove(&hw->h_entries);
         free(hw->h_lport.p_lastframe);
         free(hw);
     }
 
-    LIST_FOR_EACH_SAFE (chassis, chassis_next, list, &cfg->g_chassis) {
+    LIST_FOR_EACH_SAFE (chassis, list, &cfg->g_chassis) {
         ovs_list_remove(&chassis->list);
         free(chassis);
     }
diff --git a/lib/ovs-numa.h b/lib/ovs-numa.h
index ecc251a7ff..83bd10cca5 100644
--- a/lib/ovs-numa.h
+++ b/lib/ovs-numa.h
@@ -68,9 +68,9 @@ void ovs_numa_dump_destroy(struct ovs_numa_dump *);
 int ovs_numa_thread_setaffinity_core(unsigned core_id);
 
 #define FOR_EACH_CORE_ON_DUMP(ITER, DUMP)                    \
-    HMAP_FOR_EACH((ITER), hmap_node, &(DUMP)->cores)
+    HMAP_FOR_EACH (ITER, hmap_node, &(DUMP)->cores)
 
 #define FOR_EACH_NUMA_ON_DUMP(ITER, DUMP)                    \
-    HMAP_FOR_EACH((ITER), hmap_node, &(DUMP)->numas)
+    HMAP_FOR_EACH (ITER, hmap_node, &(DUMP)->numas)
 
 #endif /* ovs-numa.h */
diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c
index dead31275d..9713c7dc7c 100644
--- a/lib/ovsdb-cs.c
+++ b/lib/ovsdb-cs.c
@@ -900,8 +900,8 @@ ovsdb_cs_db_get_table(struct ovsdb_cs_db *db, const char *table)
 static void
 ovsdb_cs_db_destroy_tables(struct ovsdb_cs_db *db)
 {
-    struct ovsdb_cs_db_table *table, *next;
-    HMAP_FOR_EACH_SAFE (table, next, hmap_node, &db->tables) {
+    struct ovsdb_cs_db_table *table;
+    HMAP_FOR_EACH_SAFE (table, hmap_node, &db->tables) {
         json_destroy(table->ack_cond);
         json_destroy(table->req_cond);
         json_destroy(table->new_cond);
@@ -1793,8 +1793,8 @@ ovsdb_cs_update_server_row(struct server_row *row,
 static void
 ovsdb_cs_clear_server_rows(struct ovsdb_cs *cs)
 {
-    struct server_row *row, *next;
-    HMAP_FOR_EACH_SAFE (row, next, hmap_node, &cs->server_rows) {
+    struct server_row *row;
+    HMAP_FOR_EACH_SAFE (row, hmap_node, &cs->server_rows) {
         ovsdb_cs_delete_server_row(cs, row);
     }
 }
@@ -2128,9 +2128,9 @@ void
 ovsdb_cs_free_schema(struct shash *schema)
 {
     if (schema) {
-        struct shash_node *node, *next;
+        struct shash_node *node;
 
-        SHASH_FOR_EACH_SAFE (node, next, schema) {
+        SHASH_FOR_EACH_SAFE (node, schema) {
             struct sset *sset = node->data;
             sset_destroy(sset);
             free(sset);
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
index c19128d55c..882ede7559 100644
--- a/lib/ovsdb-idl.c
+++ b/lib/ovsdb-idl.c
@@ -389,25 +389,25 @@ ovsdb_idl_clear(struct ovsdb_idl *db)
      */
     for (size_t i = 0; i < db->class_->n_tables; i++) {
         struct ovsdb_idl_table *table = &db->tables[i];
-        struct ovsdb_idl_row *row, *next_row;
+        struct ovsdb_idl_row *row;
 
         if (hmap_is_empty(&table->rows)) {
             continue;
         }
 
-        HMAP_FOR_EACH_SAFE (row, next_row, hmap_node, &table->rows) {
-            struct ovsdb_idl_arc *arc, *next_arc;
+        HMAP_FOR_EACH_SAFE (row, hmap_node, &table->rows) {
+            struct ovsdb_idl_arc *arc;
 
             if (!ovsdb_idl_row_is_orphan(row)) {
                 ovsdb_idl_remove_from_indexes(row);
                 ovsdb_idl_row_unparse(row);
             }
-            LIST_FOR_EACH_SAFE (arc, next_arc, src_node, &row->src_arcs) {
+            LIST_FOR_EACH_SAFE (arc, src_node, &row->src_arcs) {
                 ovs_list_remove(&arc->src_node);
                 ovs_list_remove(&arc->dst_node);
                 free(arc);
             }
-            LIST_FOR_EACH_SAFE (arc, next_arc, dst_node, &row->dst_arcs) {
+            LIST_FOR_EACH_SAFE (arc, dst_node, &row->dst_arcs) {
                 ovs_list_remove(&arc->src_node);
                 ovs_list_remove(&arc->dst_node);
                 free(arc);
@@ -1041,8 +1041,8 @@ ovsdb_idl_condition_destroy(struct ovsdb_idl_condition *cond)
 void
 ovsdb_idl_condition_clear(struct ovsdb_idl_condition *cond)
 {
-    struct ovsdb_idl_clause *clause, *next;
-    HMAP_FOR_EACH_SAFE (clause, next, hmap_node, &cond->clauses) {
+    struct ovsdb_idl_clause *clause;
+    HMAP_FOR_EACH_SAFE (clause, hmap_node, &cond->clauses) {
         hmap_remove(&cond->clauses, &clause->hmap_node);
         ovsdb_idl_clause_destroy(clause);
     }
@@ -1345,9 +1345,9 @@ ovsdb_idl_track_clear__(struct ovsdb_idl *idl, bool flush_all)
         struct ovsdb_idl_table *table = &idl->tables[i];
 
         if (!ovs_list_is_empty(&table->track_list)) {
-            struct ovsdb_idl_row *row, *next;
+            struct ovsdb_idl_row *row;
 
-            LIST_FOR_EACH_SAFE(row, next, track_node, &table->track_list) {
+            LIST_FOR_EACH_SAFE (row, track_node, &table->track_list) {
                 if (row->updated) {
                     free(row->updated);
                     row->updated = NULL;
@@ -1480,9 +1480,9 @@ ovsdb_idl_parse_update(struct ovsdb_idl *idl,
 static void
 ovsdb_idl_reparse_deleted(struct ovsdb_idl *db)
 {
-    struct ovsdb_idl_row *row, *next;
+    struct ovsdb_idl_row *row;
 
-    LIST_FOR_EACH_SAFE (row, next, track_node, &db->deleted_untracked_rows) {
+    LIST_FOR_EACH_SAFE (row, track_node, &db->deleted_untracked_rows) {
         ovsdb_idl_row_untrack_change(row);
         add_tracked_change_for_references(row);
         ovsdb_idl_row_reparse_backrefs(row);
@@ -1906,8 +1906,8 @@ ovsdb_idl_index_create2(struct ovsdb_idl *idl,
 static void
 ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *table)
 {
-    struct ovsdb_idl_index *index, *next;
-    LIST_FOR_EACH_SAFE (index, next, node, &table->indexes) {
+    struct ovsdb_idl_index *index;
+    LIST_FOR_EACH_SAFE (index, node, &table->indexes) {
         skiplist_destroy(index->skiplist, NULL);
         free(index->columns);
         free(index);
@@ -2145,12 +2145,12 @@ ovsdb_idl_row_clear_new(struct ovsdb_idl_row *row)
 static void
 ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts)
 {
-    struct ovsdb_idl_arc *arc, *next;
+    struct ovsdb_idl_arc *arc;
 
     /* Delete all forward arcs.  If 'destroy_dsts', destroy any orphaned rows
      * that this causes to be unreferenced.
      */
-    LIST_FOR_EACH_SAFE (arc, next, src_node, &row->src_arcs) {
+    LIST_FOR_EACH_SAFE (arc, src_node, &row->src_arcs) {
         ovs_list_remove(&arc->dst_node);
         if (destroy_dsts
             && ovsdb_idl_row_is_orphan(arc->dst)
@@ -2166,7 +2166,7 @@ ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts)
 static void
 ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row)
 {
-    struct ovsdb_idl_arc *arc, *next;
+    struct ovsdb_idl_arc *arc;
 
     /* This is trickier than it looks.  ovsdb_idl_row_clear_arcs() will destroy
      * 'arc', so we need to use the "safe" variant of list traversal.  However,
@@ -2178,7 +2178,7 @@ ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row)
      * (If duplicate arcs were possible then we would need to make sure that
      * 'next' didn't also point into 'arc''s destination, but we forbid
      * duplicate arcs.) */
-    LIST_FOR_EACH_SAFE (arc, next, dst_node, &row->dst_arcs) {
+    LIST_FOR_EACH_SAFE (arc, dst_node, &row->dst_arcs) {
         struct ovsdb_idl_row *ref = arc->src;
 
         ovsdb_idl_row_unparse(ref);
@@ -2329,9 +2329,9 @@ ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl *idl)
         struct ovsdb_idl_table *table = &idl->tables[i];
 
         if (!ovs_list_is_empty(&table->track_list)) {
-            struct ovsdb_idl_row *row, *next;
+            struct ovsdb_idl_row *row;
 
-            LIST_FOR_EACH_SAFE(row, next, track_node, &table->track_list) {
+            LIST_FOR_EACH_SAFE (row, track_node, &table->track_list) {
                 if (!ovsdb_idl_track_is_set(row->table)) {
                     ovs_list_remove(&row->track_node);
                     ovsdb_idl_row_unparse(row);
@@ -2729,7 +2729,7 @@ ovsdb_idl_txn_increment(struct ovsdb_idl_txn *txn,
 void
 ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn)
 {
-    struct ovsdb_idl_txn_insert *insert, *next;
+    struct ovsdb_idl_txn_insert *insert;
 
     if (txn->status == TXN_INCOMPLETE) {
         ovsdb_cs_forget_transaction(txn->idl->cs, txn->request_id);
@@ -2739,7 +2739,7 @@ ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn)
     ovsdb_idl_txn_abort(txn);
     ds_destroy(&txn->comment);
     free(txn->error);
-    HMAP_FOR_EACH_SAFE (insert, next, hmap_node, &txn->inserted_rows) {
+    HMAP_FOR_EACH_SAFE (insert, hmap_node, &txn->inserted_rows) {
         free(insert);
     }
     hmap_destroy(&txn->inserted_rows);
@@ -2824,7 +2824,7 @@ substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn)
 static void
 ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn)
 {
-    struct ovsdb_idl_row *row, *next;
+    struct ovsdb_idl_row *row;
 
     /* This must happen early.  Otherwise, ovsdb_idl_row_parse() will call an
      * ovsdb_idl_column's 'parse' function, which will call
@@ -2832,7 +2832,7 @@ ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn)
      * transaction and fail to update the graph.  */
     txn->idl->txn = NULL;
 
-    HMAP_FOR_EACH_SAFE (row, next, txn_node, &txn->txn_rows) {
+    HMAP_FOR_EACH_SAFE (row, txn_node, &txn->txn_rows) {
         enum { INSERTED, MODIFIED, DELETED } op
             = (!row->new_datum ? DELETED
                : !row->old_datum ? INSERTED
diff --git a/lib/ovsdb-map-op.c b/lib/ovsdb-map-op.c
index 7b90ba84f9..795066e8ef 100644
--- a/lib/ovsdb-map-op.c
+++ b/lib/ovsdb-map-op.c
@@ -91,8 +91,8 @@ map_op_list_create(void)
 void
 map_op_list_destroy(struct map_op_list *list, const struct ovsdb_type *type)
 {
-    struct map_op *map_op, *next;
-    HMAP_FOR_EACH_SAFE (map_op, next, node, &list->hmap) {
+    struct map_op *map_op;
+    HMAP_FOR_EACH_SAFE (map_op, node, &list->hmap) {
         map_op_destroy(map_op, type);
     }
     hmap_destroy(&list->hmap);
diff --git a/lib/ovsdb-set-op.c b/lib/ovsdb-set-op.c
index 62c4621181..321043282e 100644
--- a/lib/ovsdb-set-op.c
+++ b/lib/ovsdb-set-op.c
@@ -90,8 +90,8 @@ set_op_list_create(void)
 void
 set_op_list_destroy(struct set_op_list *list, const struct ovsdb_type *type)
 {
-    struct set_op *set_op, *next;
-    HMAP_FOR_EACH_SAFE (set_op, next, node, &list->hmap) {
+    struct set_op *set_op;
+    HMAP_FOR_EACH_SAFE (set_op, node, &list->hmap) {
         set_op_destroy(set_op, type);
     }
     hmap_destroy(&list->hmap);
diff --git a/lib/pcap-file.c b/lib/pcap-file.c
index 41835f6f4d..3ed7ea4880 100644
--- a/lib/pcap-file.c
+++ b/lib/pcap-file.c
@@ -344,9 +344,9 @@ tcp_reader_open(void)
 void
 tcp_reader_close(struct tcp_reader *r)
 {
-    struct tcp_stream *stream, *next_stream;
+    struct tcp_stream *stream;
 
-    HMAP_FOR_EACH_SAFE (stream, next_stream, hmap_node, &r->streams) {
+    HMAP_FOR_EACH_SAFE (stream, hmap_node, &r->streams) {
         tcp_stream_destroy(r, stream);
     }
     hmap_destroy(&r->streams);
diff --git a/lib/perf-counter.c b/lib/perf-counter.c
index e4eca58d03..6952fcb594 100644
--- a/lib/perf-counter.c
+++ b/lib/perf-counter.c
@@ -178,14 +178,14 @@ perf_counters_clear(void)
 void
 perf_counters_destroy(void)
 {
-    struct shash_node *node, *next;
+    struct shash_node *node;
 
     if (fd__ != -1) {
         ioctl(fd__, PERF_EVENT_IOC_DISABLE, 0);
         close(fd__);
     }
 
-    SHASH_FOR_EACH_SAFE (node, next, &perf_counters) {
+    SHASH_FOR_EACH_SAFE (node, &perf_counters) {
         shash_delete(&perf_counters, node);
     }
 
diff --git a/lib/poll-loop.c b/lib/poll-loop.c
index 4e751ff2c7..70fabeb8a3 100644
--- a/lib/poll-loop.c
+++ b/lib/poll-loop.c
@@ -298,9 +298,9 @@ log_wakeup(const char *where, const struct pollfd *pollfd, int timeout)
 static void
 free_poll_nodes(struct poll_loop *loop)
 {
-    struct poll_node *node, *next;
+    struct poll_node *node;
 
-    HMAP_FOR_EACH_SAFE (node, next, hmap_node, &loop->poll_nodes) {
+    HMAP_FOR_EACH_SAFE (node, hmap_node, &loop->poll_nodes) {
         hmap_remove(&loop->poll_nodes, &node->hmap_node);
 #ifdef _WIN32
         if (node->wevent && node->pollfd.fd) {
diff --git a/lib/rculist.h b/lib/rculist.h
index 1072b87af2..c0d77acf94 100644
--- a/lib/rculist.h
+++ b/lib/rculist.h
@@ -365,35 +365,57 @@ rculist_is_singleton_protected(const struct rculist *list)
     return list_next == list->prev && list_next != list;
 }
 
-#define RCULIST_FOR_EACH(ITER, MEMBER, RCULIST)                         \
-    for (INIT_CONTAINER(ITER, rculist_next(RCULIST), MEMBER);           \
-         &(ITER)->MEMBER != (RCULIST);                                  \
-         ASSIGN_CONTAINER(ITER, rculist_next(&(ITER)->MEMBER), MEMBER))
-#define RCULIST_FOR_EACH_CONTINUE(ITER, MEMBER, RCULIST)                \
-    for (ASSIGN_CONTAINER(ITER, rculist_next(&(ITER)->MEMBER), MEMBER); \
-         &(ITER)->MEMBER != (RCULIST);                                  \
-         ASSIGN_CONTAINER(ITER, rculist_next(&(ITER)->MEMBER), MEMBER))
-
-#define RCULIST_FOR_EACH_REVERSE_PROTECTED(ITER, MEMBER, RCULIST)       \
-    for (INIT_CONTAINER(ITER, (RCULIST)->prev, MEMBER);                 \
-         &(ITER)->MEMBER != (RCULIST);                                  \
-         ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER))
-#define RCULIST_FOR_EACH_REVERSE_PROTECTED_CONTINUE(ITER, MEMBER, RCULIST) \
-    for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER);           \
-         &(ITER)->MEMBER != (RCULIST);                                  \
-         ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER))
-
-#define RCULIST_FOR_EACH_PROTECTED(ITER, MEMBER, RCULIST)               \
-    for (INIT_CONTAINER(ITER, rculist_next_protected(RCULIST), MEMBER); \
-         &(ITER)->MEMBER != (RCULIST);                                  \
-         ASSIGN_CONTAINER(ITER, rculist_next_protected(&(ITER)->MEMBER), \
-                          MEMBER))
-
-#define RCULIST_FOR_EACH_SAFE_PROTECTED(ITER, NEXT, MEMBER, RCULIST)    \
-    for (INIT_CONTAINER(ITER, rculist_next_protected(RCULIST), MEMBER); \
-         (&(ITER)->MEMBER != (RCULIST)                                  \
-          ? INIT_CONTAINER(NEXT, rculist_next_protected(&(ITER)->MEMBER), \
-                           MEMBER), 1 : 0);                             \
-         (ITER) = (NEXT))
+#define RCULIST_FOR_EACH(ITER, MEMBER, RCULIST)                               \
+    for (INIT_MULTIVAR(ITER, MEMBER, rculist_next(RCULIST),                   \
+                       const struct rculist);                                 \
+         CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST));       \
+         UPDATE_MULTIVAR(ITER, rculist_next(ITER_VAR(ITER))))
+
+#define RCULIST_FOR_EACH_CONTINUE(ITER, MEMBER, RCULIST)                      \
+    for (INIT_MULTIVAR(ITER, MEMBER, rculist_next(&(ITER)->MEMBER),           \
+                       const struct rculist);                                 \
+         CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST));       \
+         UPDATE_MULTIVAR(ITER, rculist_next(ITER_VAR(ITER))))
+
+#define RCULIST_FOR_EACH_REVERSE_PROTECTED(ITER, MEMBER, RCULIST)             \
+    for (INIT_MULTIVAR(ITER, MEMBER, (RCULIST)->prev, struct rculist);        \
+         CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST));       \
+         UPDATE_MULTIVAR(ITER, ITER_VAR(VAR).prev))
+
+#define RCULIST_FOR_EACH_REVERSE_PROTECTED_CONTINUE(ITER, MEMBER, RCULIST)    \
+    for (INIT_MULTIVAR(ITER, MEMBER, (ITER)->MEMBER.prev, struct rculist);    \
+         CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST));       \
+         UPDATE_MULTIVAR(ITER, ITER_VAR(VAR).prev))
+
+#define RCULIST_FOR_EACH_PROTECTED(ITER, MEMBER, RCULIST)                     \
+    for (INIT_MULTIVAR(ITER, MEMBER, rculist_next_protected(RCULIST),         \
+                       struct rculist);                                       \
+         CONDITION_MULTIVAR(ITER, MEMBER, ITER_VAR(ITER) != (RCULIST));       \
+         UPDATE_MULTIVAR(ITER, rculist_next_protected(ITER_VAR(ITER)))        \
+
+#define RCULIST_FOR_EACH_SAFE_SHORT_PROTECTED(ITER, MEMBER, RCULIST)          \
+    for (INIT_MULTIVAR_SAFE_SHORT(ITER, MEMBER,                               \
+                                  rculist_next_protected(RCULIST),            \
+                                  struct rculist);                            \
+         CONDITION_MULTIVAR_SAFE_SHORT(ITER, MEMBER,                          \
+                                       ITER_VAR(ITER) != (RCULIST),           \
+             ITER_NEXT_VAR(ITER) = rculist_next_protected(ITER_VAR(VAR)));    \
+        UPDATE_MULTIVAR_SHORT(ITER))
+
+#define RCULIST_FOR_EACH_SAFE_LONG_PROTECTED(ITER, NEXT, MEMBER, RCULIST)     \
+    for (INIT_MULTIVAR_SAFE_LONG(ITER, NEXT, MEMBER,                          \
+                                 rculist_next_protected(RCULIST)              \
+                                 struct rculist);                             \
+         CONDITION_MULTIVAR_SAFE_LONG(VAR, NEXT, MEMBER                       \
+                                      ITER_VAR(ITER) != (RCULIST),            \
+             ITER_VAR(NEXT) = rculist_next_protected(ITER_VAR(VAR)),          \
+                                      ITER_VAR(NEXT) != (RCULIST));           \
+        UPDATE_MULTIVAR_LONG(ITER))
+
+#define RCULIST_FOR_EACH_SAFE_PROTECTED(...)                                  \
+    OVERLOAD_SAFE_MACRO(RCULIST_FOR_EACH_SAFE_LONG_PROTECTED,                 \
+                        RCULIST_FOR_EACH_SAFE_SHORT_PROTECTED,                \
+                        4, __VA_ARGS__)
+
 
 #endif /* rculist.h */
diff --git a/lib/reconnect.c b/lib/reconnect.c
index a929ddfd2d..89a0bcaf95 100644
--- a/lib/reconnect.c
+++ b/lib/reconnect.c
@@ -75,7 +75,8 @@ struct reconnect {
 
 static void reconnect_transition__(struct reconnect *, long long int now,
                                    enum state state);
-static long long int reconnect_deadline__(const struct reconnect *);
+static long long int reconnect_deadline__(const struct reconnect *,
+                                          long long int now);
 static bool reconnect_may_retry(struct reconnect *);
 
 static const char *
@@ -539,7 +540,7 @@ reconnect_transition__(struct reconnect *fsm, long long int now,
 }
 
 static long long int
-reconnect_deadline__(const struct reconnect *fsm)
+reconnect_deadline__(const struct reconnect *fsm, long long int now)
 {
     ovs_assert(fsm->state_entered != LLONG_MIN);
     switch (fsm->state) {
@@ -557,8 +558,18 @@ reconnect_deadline__(const struct reconnect *fsm)
         if (fsm->probe_interval) {
             long long int base = MAX(fsm->last_activity, fsm->state_entered);
             long long int expiration = base + fsm->probe_interval;
-            if (fsm->last_receive_attempt >= expiration) {
+            if (now < expiration || fsm->last_receive_attempt >= expiration) {
+                /* We still have time before the expiration or the time has
+                 * already passed and there was no activity.  In the first case
+                 * we need to wait for the expiration, in the second - we're
+                 * already past the deadline. */
                 return expiration;
+            } else {
+                /* Time has already passed, but we didn't attempt to receive
+                 * anything.  We need to wake up and try to receive even if
+                 * nothing is pending, so we can update the expiration time or
+                 * transition to a different state. */
+                return now + 1;
             }
         }
         return LLONG_MAX;
@@ -566,8 +577,10 @@ reconnect_deadline__(const struct reconnect *fsm)
     case S_IDLE:
         if (fsm->probe_interval) {
             long long int expiration = fsm->state_entered + fsm->probe_interval;
-            if (fsm->last_receive_attempt >= expiration) {
+            if (now < expiration || fsm->last_receive_attempt >= expiration) {
                 return expiration;
+            } else {
+                return now + 1;
             }
         }
         return LLONG_MAX;
@@ -618,7 +631,7 @@ reconnect_deadline__(const struct reconnect *fsm)
 enum reconnect_action
 reconnect_run(struct reconnect *fsm, long long int now)
 {
-    if (now >= reconnect_deadline__(fsm)) {
+    if (now >= reconnect_deadline__(fsm, now)) {
         switch (fsm->state) {
         case S_VOID:
             return 0;
@@ -671,7 +684,7 @@ reconnect_wait(struct reconnect *fsm, long long int now)
 int
 reconnect_timeout(struct reconnect *fsm, long long int now)
 {
-    long long int deadline = reconnect_deadline__(fsm);
+    long long int deadline = reconnect_deadline__(fsm, now);
     if (deadline != LLONG_MAX) {
         long long int remaining = deadline - now;
         return MAX(0, MIN(INT_MAX, remaining));
diff --git a/lib/seq.c b/lib/seq.c
index 6581cb06ba..99e5bf8bd1 100644
--- a/lib/seq.c
+++ b/lib/seq.c
@@ -297,9 +297,9 @@ static void
 seq_thread_woke(struct seq_thread *thread)
     OVS_REQUIRES(seq_mutex)
 {
-    struct seq_waiter *waiter, *next_waiter;
+    struct seq_waiter *waiter;
 
-    LIST_FOR_EACH_SAFE (waiter, next_waiter, list_node, &thread->waiters) {
+    LIST_FOR_EACH_SAFE (waiter, list_node, &thread->waiters) {
         ovs_assert(waiter->thread == thread);
         seq_waiter_destroy(waiter);
     }
@@ -319,9 +319,9 @@ static void
 seq_wake_waiters(struct seq *seq)
     OVS_REQUIRES(seq_mutex)
 {
-    struct seq_waiter *waiter, *next_waiter;
+    struct seq_waiter *waiter;
 
-    HMAP_FOR_EACH_SAFE (waiter, next_waiter, hmap_node, &seq->waiters) {
+    HMAP_FOR_EACH_SAFE (waiter, hmap_node, &seq->waiters) {
         latch_set(&waiter->thread->latch);
         seq_waiter_destroy(waiter);
     }
diff --git a/lib/shash.c b/lib/shash.c
index a8433629ab..a7b2c64582 100644
--- a/lib/shash.c
+++ b/lib/shash.c
@@ -68,9 +68,9 @@ shash_moved(struct shash *sh)
 void
 shash_clear(struct shash *sh)
 {
-    struct shash_node *node, *next;
+    struct shash_node *node;
 
-    SHASH_FOR_EACH_SAFE (node, next, sh) {
+    SHASH_FOR_EACH_SAFE (node, sh) {
         hmap_remove(&sh->map, &node->node);
         free(node->name);
         free(node);
@@ -81,9 +81,9 @@ shash_clear(struct shash *sh)
 void
 shash_clear_free_data(struct shash *sh)
 {
-    struct shash_node *node, *next;
+    struct shash_node *node;
 
-    SHASH_FOR_EACH_SAFE (node, next, sh) {
+    SHASH_FOR_EACH_SAFE (node, sh) {
         hmap_remove(&sh->map, &node->node);
         free(node->data);
         free(node->name);
diff --git a/lib/simap.c b/lib/simap.c
index f404ece677..0ee08d74d5 100644
--- a/lib/simap.c
+++ b/lib/simap.c
@@ -63,9 +63,9 @@ simap_moved(struct simap *simap)
 void
 simap_clear(struct simap *simap)
 {
-    struct simap_node *node, *next;
+    struct simap_node *node;
 
-    SIMAP_FOR_EACH_SAFE (node, next, simap) {
+    SIMAP_FOR_EACH_SAFE (node, simap) {
         hmap_remove(&simap->map, &node->node);
         free(node->name);
         free(node);
diff --git a/lib/simap.h b/lib/simap.h
index 5e646e6607..8db7bea7c9 100644
--- a/lib/simap.h
+++ b/lib/simap.h
@@ -41,12 +41,22 @@ struct simap_node {
                         BUILD_ASSERT_TYPE(SIMAP_NODE, struct simap_node *), \
                         BUILD_ASSERT_TYPE(SIMAP, struct simap *))
 
-#define SIMAP_FOR_EACH_SAFE(SIMAP_NODE, NEXT, SIMAP)                    \
-    HMAP_FOR_EACH_SAFE_INIT (SIMAP_NODE, NEXT, node, &(SIMAP)->map,     \
+#define SIMAP_FOR_EACH_SAFE_SHORT(SIMAP_NODE, SIMAP)                        \
+    HMAP_FOR_EACH_SAFE_SHORT_INIT (SIMAP_NODE, node, &(SIMAP)->map,         \
                         BUILD_ASSERT_TYPE(SIMAP_NODE, struct simap_node *), \
-                        BUILD_ASSERT_TYPE(NEXT, struct simap_node *),   \
                         BUILD_ASSERT_TYPE(SIMAP, struct simap *))
 
+#define SIMAP_FOR_EACH_SAFE_LONG(SIMAP_NODE, NEXT, SIMAP)                    \
+    HMAP_FOR_EACH_SAFE_LONG_INIT (SIMAP_NODE, NEXT, node, &(SIMAP)->map,     \
+                        BUILD_ASSERT_TYPE(SIMAP_NODE, struct simap_node *),  \
+                        BUILD_ASSERT_TYPE(NEXT, struct simap_node *),        \
+                        BUILD_ASSERT_TYPE(SIMAP, struct simap *))
+
+#define SIMAP_FOR_EACH_SAFE(...)                                              \
+    OVERLOAD_SAFE_MACRO(SIMAP_FOR_EACH_SAFE_LONG,                             \
+                        SIMAP_FOR_EACH_SAFE_SHORT,                            \
+                        3, __VA_ARGS__)
+
 void simap_init(struct simap *);
 void simap_destroy(struct simap *);
 void simap_swap(struct simap *, struct simap *);
diff --git a/lib/smap.c b/lib/smap.c
index e82261497c..b23eeb52d3 100644
--- a/lib/smap.c
+++ b/lib/smap.c
@@ -185,9 +185,9 @@ smap_steal(struct smap *smap, struct smap_node *node,
 void
 smap_clear(struct smap *smap)
 {
-    struct smap_node *node, *next;
+    struct smap_node *node;
 
-    SMAP_FOR_EACH_SAFE (node, next, smap) {
+    SMAP_FOR_EACH_SAFE (node, smap) {
         smap_remove_node(smap, node);
     }
 }
diff --git a/lib/smap.h b/lib/smap.h
index a921159667..2fe6c540a7 100644
--- a/lib/smap.h
+++ b/lib/smap.h
@@ -45,13 +45,24 @@ struct smap_node {
                         BUILD_ASSERT_TYPE(SMAP_NODE, struct smap_node *), \
                         BUILD_ASSERT_TYPE(SMAP, struct smap *))
 
-#define SMAP_FOR_EACH_SAFE(SMAP_NODE, NEXT, SMAP)           \
-    HMAP_FOR_EACH_SAFE_INIT (                               \
+#define SMAP_FOR_EACH_SAFE_SHORT(SMAP_NODE, SMAP)           \
+    HMAP_FOR_EACH_SAFE_SHORT_INIT (                         \
+        SMAP_NODE, node, &(SMAP)->map,                      \
+        BUILD_ASSERT_TYPE(SMAP_NODE, struct smap_node *),   \
+        BUILD_ASSERT_TYPE(SMAP, struct smap *))
+
+#define SMAP_FOR_EACH_SAFE_LONG(SMAP_NODE, NEXT, SMAP)      \
+    HMAP_FOR_EACH_SAFE_LONG_INIT (                          \
         SMAP_NODE, NEXT, node, &(SMAP)->map,                \
         BUILD_ASSERT_TYPE(SMAP_NODE, struct smap_node *),   \
         BUILD_ASSERT_TYPE(NEXT, struct smap_node *),        \
         BUILD_ASSERT_TYPE(SMAP, struct smap *))
 
+#define SMAP_FOR_EACH_SAFE(...)                                               \
+    OVERLOAD_SAFE_MACRO(SMAP_FOR_EACH_SAFE_LONG,                              \
+                        SMAP_FOR_EACH_SAFE_SHORT,                             \
+                        3, __VA_ARGS__)
+
 /* Initializer for an immutable struct smap 'SMAP' that contains one or two
  * key-value pairs, e.g.
  *
diff --git a/lib/socket-util.c b/lib/socket-util.c
index 4f1ffecf5d..38705cc51e 100644
--- a/lib/socket-util.c
+++ b/lib/socket-util.c
@@ -62,7 +62,8 @@ static bool parse_sockaddr_components(struct sockaddr_storage *ss,
                                       const char *port_s,
                                       uint16_t default_port,
                                       const char *s,
-                                      bool resolve_host);
+                                      bool resolve_host,
+                                      bool *dns_failure);
 
 /* Sets 'fd' to non-blocking mode.  Returns 0 if successful, otherwise a
  * positive errno value. */
@@ -438,7 +439,7 @@ parse_sockaddr_components_dns(struct sockaddr_storage *ss OVS_UNUSED,
     dns_resolve(host_s, &tmp_host_s);
     if (tmp_host_s != NULL) {
         parse_sockaddr_components(ss, tmp_host_s, port_s,
-                                  default_port, s, false);
+                                  default_port, s, false, NULL);
         free(tmp_host_s);
         return true;
     }
@@ -450,11 +451,15 @@ parse_sockaddr_components(struct sockaddr_storage *ss,
                           char *host_s,
                           const char *port_s, uint16_t default_port,
                           const char *s,
-                          bool resolve_host)
+                          bool resolve_host, bool *dns_failure)
 {
     struct sockaddr_in *sin = sin_cast(sa_cast(ss));
     int port;
 
+    if (dns_failure) {
+        *dns_failure = false;
+    }
+
     if (port_s && port_s[0]) {
         if (!str_to_int(port_s, 10, &port) || port < 0 || port > 65535) {
             VLOG_ERR("%s: bad port number \"%s\"", s, port_s);
@@ -501,10 +506,15 @@ parse_sockaddr_components(struct sockaddr_storage *ss,
     return true;
 
 resolve:
-    if (resolve_host && parse_sockaddr_components_dns(ss, host_s, port_s,
-                                                      default_port, s)) {
-        return true;
-    } else if (!resolve_host) {
+    if (resolve_host) {
+        if (parse_sockaddr_components_dns(ss, host_s, port_s,
+                                          default_port, s)) {
+            return true;
+        }
+        if (dns_failure) {
+            *dns_failure = true;
+        }
+    } else {
         VLOG_ERR("%s: bad IP address \"%s\"", s, host_s);
     }
 exit:
@@ -521,10 +531,12 @@ exit:
  * It resolves the host if 'resolve_host' is true.
  *
  * On success, returns true and stores the parsed remote address into '*ss'.
- * On failure, logs an error, stores zeros into '*ss', and returns false. */
+ * On failure, logs an error, stores zeros into '*ss', and returns false,
+ * '*dns_failure' indicates if the host resolution failed. */
 bool
 inet_parse_active(const char *target_, int default_port,
-                  struct sockaddr_storage *ss, bool resolve_host)
+                  struct sockaddr_storage *ss,
+                  bool resolve_host, bool *dns_failure)
 {
     char *target = xstrdup(target_);
     char *port, *host;
@@ -539,7 +551,7 @@ inet_parse_active(const char *target_, int default_port,
         ok = false;
     } else {
         ok = parse_sockaddr_components(ss, host, port, default_port,
-                                       target_, resolve_host);
+                                       target_, resolve_host, dns_failure);
     }
     if (!ok) {
         memset(ss, 0, sizeof *ss);
@@ -576,7 +588,7 @@ inet_open_active(int style, const char *target, int default_port,
     int error;
 
     /* Parse. */
-    if (!inet_parse_active(target, default_port, &ss, true)) {
+    if (!inet_parse_active(target, default_port, &ss, true, NULL)) {
         error = EAFNOSUPPORT;
         goto exit;
     }
@@ -660,7 +672,7 @@ inet_parse_passive(const char *target_, int default_port,
         ok = false;
     } else {
         ok = parse_sockaddr_components(ss, host, port, default_port,
-                                       target_, true);
+                                       target_, true, NULL);
     }
     if (!ok) {
         memset(ss, 0, sizeof *ss);
@@ -783,7 +795,8 @@ inet_parse_address(const char *target_, struct sockaddr_storage *ss)
 {
     char *target = xstrdup(target_);
     char *host = unbracket(target);
-    bool ok = parse_sockaddr_components(ss, host, NULL, 0, target_, false);
+    bool ok = parse_sockaddr_components(ss, host, NULL, 0,
+                                        target_, false, NULL);
     if (!ok) {
         memset(ss, 0, sizeof *ss);
     }
diff --git a/lib/socket-util.h b/lib/socket-util.h
index 9ccb7d4cc4..bf66393df9 100644
--- a/lib/socket-util.h
+++ b/lib/socket-util.h
@@ -49,7 +49,8 @@ ovs_be32 guess_netmask(ovs_be32 ip);
 void inet_parse_host_port_tokens(char *s, char **hostp, char **portp);
 void inet_parse_port_host_tokens(char *s, char **portp, char **hostp);
 bool inet_parse_active(const char *target, int default_port,
-                       struct sockaddr_storage *ssp, bool resolve_host);
+                       struct sockaddr_storage *ssp,
+                       bool resolve_host, bool *dns_failure);
 int inet_open_active(int style, const char *target, int default_port,
                      struct sockaddr_storage *ssp, int *fdp, uint8_t dscp);
 
diff --git a/lib/sset.c b/lib/sset.c
index b2e3f43ec9..c3197e305f 100644
--- a/lib/sset.c
+++ b/lib/sset.c
@@ -212,9 +212,9 @@ sset_add_array(struct sset *set, char **names, size_t n)
 void
 sset_clear(struct sset *set)
 {
-    const char *name, *next;
+    const char *name;
 
-    SSET_FOR_EACH_SAFE (name, next, set) {
+    SSET_FOR_EACH_SAFE (name, set) {
         sset_delete(set, SSET_NODE_FROM_NAME(name));
     }
 }
@@ -320,9 +320,9 @@ sset_at_position(const struct sset *set, struct sset_position *pos)
 void
 sset_intersect(struct sset *a, const struct sset *b)
 {
-    const char *name, *next;
+    const char *name;
 
-    SSET_FOR_EACH_SAFE (name, next, a) {
+    SSET_FOR_EACH_SAFE (name, a) {
         if (!sset_contains(b, name)) {
             sset_delete(a, SSET_NODE_FROM_NAME(name));
         }
diff --git a/lib/sset.h b/lib/sset.h
index f0bb8b5344..214d6fb41c 100644
--- a/lib/sset.h
+++ b/lib/sset.h
@@ -87,13 +87,26 @@ void sset_intersect(struct sset *, const struct sset *);
          NAME != NULL;                          \
          (NAME) = SSET_NEXT(SSET, NAME))
 
-#define SSET_FOR_EACH_SAFE(NAME, NEXT, SSET)        \
+#define SSET_FOR_EACH_SAFE_LONG(NAME, NEXT, SSET)   \
     for ((NAME) = SSET_FIRST(SSET);                 \
          (NAME != NULL                              \
           ? (NEXT) = SSET_NEXT(SSET, NAME), true    \
           : false);                                 \
          (NAME) = (NEXT))
 
+#define SSET_FOR_EACH_SAFE_SHORT(NAME, SSET)           \
+    for (const char * NAME__next =                     \
+         ((NAME) = SSET_FIRST(SSET), NULL);            \
+         (NAME != NULL                                 \
+          ? (NAME__next = SSET_NEXT(SSET, NAME), true) \
+          : (NAME__next = NULL, false));               \
+         (NAME) = NAME__next)
+
+#define SSET_FOR_EACH_SAFE(...)                        \
+    OVERLOAD_SAFE_MACRO(SSET_FOR_EACH_SAFE_LONG,       \
+                        SSET_FOR_EACH_SAFE_SHORT,      \
+                        3, __VA_ARGS__)
+
 const char **sset_array(const struct sset *);
 const char **sset_sort(const struct sset *);
 
diff --git a/lib/stopwatch.c b/lib/stopwatch.c
index 1c71df1a12..ec567603b1 100644
--- a/lib/stopwatch.c
+++ b/lib/stopwatch.c
@@ -464,7 +464,7 @@ stopwatch_thread(void *ign OVS_UNUSED)
 static void
 stopwatch_exit(void)
 {
-    struct shash_node *node, *node_next;
+    struct shash_node *node;
     struct stopwatch_packet *pkt = stopwatch_packet_create(OP_SHUTDOWN);
     stopwatch_packet_write(pkt);
     xpthread_join(stopwatch_thread_id, NULL);
@@ -473,7 +473,7 @@ stopwatch_exit(void)
      * other competing thread. We are now the sole owners
      * of all data in the file.
      */
-    SHASH_FOR_EACH_SAFE (node, node_next, &stopwatches) {
+    SHASH_FOR_EACH_SAFE (node, &stopwatches) {
         struct stopwatch *sw = node->data;
         shash_delete(&stopwatches, node);
         free(sw);
diff --git a/lib/stream.c b/lib/stream.c
index fcaddf10ad..71039e24f1 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -788,7 +788,7 @@ stream_parse_target_with_default_port(const char *target, int default_port,
                                       struct sockaddr_storage *ss)
 {
     return ((!strncmp(target, "tcp:", 4) || !strncmp(target, "ssl:", 4))
-            && inet_parse_active(target + 4, default_port, ss, true));
+            && inet_parse_active(target + 4, default_port, ss, true, NULL));
 }
 
 /* Attempts to guess the content type of a stream whose first few bytes were
diff --git a/lib/tc.c b/lib/tc.c
index adb2d3182a..df73a43d4c 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -568,16 +568,17 @@ nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
 
     flower->key.encap_eth_type[0] =
         nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]);
+    flower->mask.encap_eth_type[0] = CONSTANT_HTONS(0xffff);
 
     if (attrs[TCA_FLOWER_KEY_VLAN_ID]) {
         flower->key.vlan_id[0] =
             nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]);
-        flower->mask.vlan_id[0] = 0xffff;
+        flower->mask.vlan_id[0] = VLAN_VID_MASK >> VLAN_VID_SHIFT;
     }
     if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) {
         flower->key.vlan_prio[0] =
             nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]);
-        flower->mask.vlan_prio[0] = 0xff;
+        flower->mask.vlan_prio[0] = VLAN_PCP_MASK >> VLAN_PCP_SHIFT;
     }
 
     if (!attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) {
@@ -590,17 +591,18 @@ nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
     }
 
     flower->key.encap_eth_type[1] = flower->key.encap_eth_type[0];
+    flower->mask.encap_eth_type[1] = CONSTANT_HTONS(0xffff);
     flower->key.encap_eth_type[0] = encap_ethtype;
 
     if (attrs[TCA_FLOWER_KEY_CVLAN_ID]) {
         flower->key.vlan_id[1] =
             nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CVLAN_ID]);
-        flower->mask.vlan_id[1] = 0xffff;
+        flower->mask.vlan_id[1] = VLAN_VID_MASK >> VLAN_VID_SHIFT;
     }
     if (attrs[TCA_FLOWER_KEY_CVLAN_PRIO]) {
         flower->key.vlan_prio[1] =
             nl_attr_get_u8(attrs[TCA_FLOWER_KEY_CVLAN_PRIO]);
-        flower->mask.vlan_prio[1] = 0xff;
+        flower->mask.vlan_prio[1] = VLAN_PCP_MASK >> VLAN_PCP_SHIFT;
     }
 }
 
@@ -937,24 +939,21 @@ nl_parse_flower_ip(struct nlattr **attrs, struct tc_flower *flower) {
             key->icmp_code =
                nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_CODE]);
             mask->icmp_code =
-                nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_CODE]);
+                nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_CODE_MASK]);
         }
         if (attrs[TCA_FLOWER_KEY_ICMPV4_TYPE_MASK]) {
-            key->icmp_type =
-               nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_TYPE_MASK]);
+            key->icmp_type = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_TYPE]);
             mask->icmp_type =
                 nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_TYPE_MASK]);
         }
     } else if (ip_proto == IPPROTO_ICMPV6) {
         if (attrs[TCA_FLOWER_KEY_ICMPV6_CODE_MASK]) {
-            key->icmp_code =
-               nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_CODE]);
+            key->icmp_code = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_CODE]);
             mask->icmp_code =
-                 nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_CODE]);
+                 nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_CODE_MASK]);
         }
         if (attrs[TCA_FLOWER_KEY_ICMPV6_TYPE_MASK]) {
-            key->icmp_type =
-               nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_TYPE_MASK]);
+            key->icmp_type = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_TYPE]);
             mask->icmp_type =
                 nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_TYPE_MASK]);
         }
@@ -1006,14 +1005,14 @@ static const struct nl_policy pedit_policy[] = {
 static int
 nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
 {
-    struct tc_action *action;
+    struct tc_action *action = &flower->actions[flower->action_count++];
     struct nlattr *pe_attrs[ARRAY_SIZE(pedit_policy)];
     const struct tc_pedit *pe;
     const struct tc_pedit_key *keys;
     const struct nlattr *nla, *keys_ex, *ex_type;
     const void *keys_attr;
-    char *rewrite_key = (void *) &flower->rewrite.key;
-    char *rewrite_mask = (void *) &flower->rewrite.mask;
+    char *rewrite_key = (void *) &action->rewrite.key;
+    char *rewrite_mask = (void *) &action->rewrite.mask;
     size_t keys_ex_size, left;
     int type, i = 0, err;
 
@@ -1092,7 +1091,6 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
         i++;
     }
 
-    action = &flower->actions[flower->action_count++];
     action->type = TC_ACT_PEDIT;
 
     return 0;
@@ -1487,7 +1485,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
                 if (ipv4_max) {
                     ovs_be32 addr = nl_attr_get_be32(ipv4_max);
 
-                    action->ct.range.ipv4.max = addr;
+                    if (action->ct.range.ipv4.min != addr) {
+                        action->ct.range.ipv4.max = addr;
+                    }
                 }
             } else if (ipv6_min) {
                 action->ct.range.ip_family = AF_INET6;
@@ -1496,7 +1496,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
                 if (ipv6_max) {
                     struct in6_addr addr = nl_attr_get_in6_addr(ipv6_max);
 
-                    action->ct.range.ipv6.max = addr;
+                    if (!ipv6_addr_equals(&action->ct.range.ipv6.min, &addr)) {
+                        action->ct.range.ipv6.max = addr;
+                    }
                 }
             }
 
@@ -1504,6 +1506,10 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
                 action->ct.range.port.min = nl_attr_get_be16(port_min);
                 if (port_max) {
                     action->ct.range.port.max = nl_attr_get_be16(port_max);
+                    if (action->ct.range.port.min ==
+                        action->ct.range.port.max) {
+                        action->ct.range.port.max = 0;
+                    }
                 }
             }
         }
@@ -1702,6 +1708,9 @@ static const struct nl_policy stats_policy[] = {
     [TCA_STATS_BASIC] = { .type = NL_A_UNSPEC,
                           .min_len = sizeof(struct gnet_stats_basic),
                           .optional = false, },
+    [TCA_STATS_BASIC_HW] = { .type = NL_A_UNSPEC,
+                             .min_len = sizeof(struct gnet_stats_basic),
+                             .optional = true, },
 };
 
 static int
@@ -1714,8 +1723,11 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
     const char *act_kind;
     struct nlattr *action_attrs[ARRAY_SIZE(act_policy)];
     struct nlattr *stats_attrs[ARRAY_SIZE(stats_policy)];
-    struct ovs_flow_stats *stats = &flower->stats;
-    const struct gnet_stats_basic *bs;
+    struct ovs_flow_stats *stats_sw = &flower->stats_sw;
+    struct ovs_flow_stats *stats_hw = &flower->stats_hw;
+    const struct gnet_stats_basic *bs_all = NULL;
+    const struct gnet_stats_basic *bs_hw = NULL;
+    struct gnet_stats_basic bs_sw = { .packets = 0, .bytes = 0, };
     int err = 0;
 
     if (!nl_parse_nested(action, act_policy, action_attrs,
@@ -1771,10 +1783,26 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
         return EPROTO;
     }
 
-    bs = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs);
-    if (bs->packets) {
-        put_32aligned_u64(&stats->n_packets, bs->packets);
-        put_32aligned_u64(&stats->n_bytes, bs->bytes);
+    bs_all = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs_all);
+    if (stats_attrs[TCA_STATS_BASIC_HW]) {
+        bs_hw = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC_HW],
+                                   sizeof *bs_hw);
+
+        bs_sw.packets = bs_all->packets - bs_hw->packets;
+        bs_sw.bytes = bs_all->bytes - bs_hw->bytes;
+    } else {
+        bs_sw.packets = bs_all->packets;
+        bs_sw.bytes = bs_all->bytes;
+    }
+
+    if (bs_sw.packets > get_32aligned_u64(&stats_sw->n_packets)) {
+        put_32aligned_u64(&stats_sw->n_packets, bs_sw.packets);
+        put_32aligned_u64(&stats_sw->n_bytes, bs_sw.bytes);
+    }
+
+    if (bs_hw && bs_hw->packets > get_32aligned_u64(&stats_hw->n_packets)) {
+        put_32aligned_u64(&stats_hw->n_packets, bs_hw->packets);
+        put_32aligned_u64(&stats_hw->n_bytes, bs_hw->bytes);
     }
 
     return 0;
@@ -2399,14 +2427,14 @@ nl_msg_put_act_flags(struct ofpbuf *request) {
  * first_word_mask/last_word_mask - the mask to use for the first/last read
  * (as we read entire words). */
 static void
-calc_offsets(struct tc_flower *flower, struct flower_key_to_pedit *m,
+calc_offsets(struct tc_action *action, struct flower_key_to_pedit *m,
              int *cur_offset, int *cnt, ovs_be32 *last_word_mask,
              ovs_be32 *first_word_mask, ovs_be32 **mask, ovs_be32 **data)
 {
     int start_offset, max_offset, total_size;
     int diff, right_zero_bits, left_zero_bits;
-    char *rewrite_key = (void *) &flower->rewrite.key;
-    char *rewrite_mask = (void *) &flower->rewrite.mask;
+    char *rewrite_key = (void *) &action->rewrite.key;
+    char *rewrite_mask = (void *) &action->rewrite.mask;
 
     max_offset = m->offset + m->size;
     start_offset = ROUND_DOWN(m->offset, 4);
@@ -2473,7 +2501,8 @@ csum_update_flag(struct tc_flower *flower,
 
 static int
 nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
-                                 struct tc_flower *flower)
+                                 struct tc_flower *flower,
+                                 struct tc_action *action)
 {
     struct {
         struct tc_pedit sel;
@@ -2497,7 +2526,7 @@ nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
             continue;
         }
 
-        calc_offsets(flower, m, &cur_offset, &cnt, &last_word_mask,
+        calc_offsets(action, m, &cur_offset, &cnt, &last_word_mask,
                      &first_word_mask, &mask, &data);
 
         for (j = 0; j < cnt; j++,  mask++, data++, cur_offset += 4) {
@@ -2556,6 +2585,29 @@ nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index)
     nl_msg_end_nested(request, act_offset);
 }
 
+/* Aggregates all previous successive pedit actions csum_update_flags
+ * to flower->csum_update_flags. Only append one csum action to the
+ * last pedit action. */
+static void
+nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower,
+                    uint16_t *act_index)
+{
+    size_t act_offset;
+
+    /* No pedit actions or processed already. */
+    if (!flower->csum_update_flags) {
+        return;
+    }
+
+    act_offset = nl_msg_start_nested(request, (*act_index)++);
+    nl_msg_put_act_csum(request, flower->csum_update_flags);
+    nl_msg_put_act_flags(request);
+    nl_msg_end_nested(request, act_offset);
+
+    /* Clear it. So we can have another series of pedit actions. */
+    flower->csum_update_flags = 0;
+}
+
 static int
 nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
 {
@@ -2572,20 +2624,22 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
 
         action = flower->actions;
         for (i = 0; i < flower->action_count; i++, action++) {
+            if (action->type != TC_ACT_PEDIT) {
+                nl_msg_put_csum_act(request, flower, &act_index);
+            }
             switch (action->type) {
             case TC_ACT_PEDIT: {
                 act_offset = nl_msg_start_nested(request, act_index++);
-                error = nl_msg_put_flower_rewrite_pedits(request, flower);
+                error = nl_msg_put_flower_rewrite_pedits(request, flower,
+                                                         action);
                 if (error) {
                     return error;
                 }
                 nl_msg_end_nested(request, act_offset);
 
-                if (flower->csum_update_flags) {
-                    act_offset = nl_msg_start_nested(request, act_index++);
-                    nl_msg_put_act_csum(request, flower->csum_update_flags);
-                    nl_msg_put_act_flags(request);
-                    nl_msg_end_nested(request, act_offset);
+                if (i == flower->action_count - 1) {
+                    /* If this is the last action check csum calc again. */
+                    nl_msg_put_csum_act(request, flower, &act_index);
                 }
             }
             break;
@@ -2914,13 +2968,13 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
             FLOWER_PUT_MASKED_VALUE(icmp_code, TCA_FLOWER_KEY_ICMPV6_CODE);
             FLOWER_PUT_MASKED_VALUE(icmp_type, TCA_FLOWER_KEY_ICMPV6_TYPE);
         }
-
-        FLOWER_PUT_MASKED_VALUE(ct_state, TCA_FLOWER_KEY_CT_STATE);
-        FLOWER_PUT_MASKED_VALUE(ct_zone, TCA_FLOWER_KEY_CT_ZONE);
-        FLOWER_PUT_MASKED_VALUE(ct_mark, TCA_FLOWER_KEY_CT_MARK);
-        FLOWER_PUT_MASKED_VALUE(ct_label, TCA_FLOWER_KEY_CT_LABELS);
     }
 
+    FLOWER_PUT_MASKED_VALUE(ct_state, TCA_FLOWER_KEY_CT_STATE);
+    FLOWER_PUT_MASKED_VALUE(ct_zone, TCA_FLOWER_KEY_CT_ZONE);
+    FLOWER_PUT_MASKED_VALUE(ct_mark, TCA_FLOWER_KEY_CT_MARK);
+    FLOWER_PUT_MASKED_VALUE(ct_label, TCA_FLOWER_KEY_CT_LABELS);
+
     if (host_eth_type == ETH_P_IP) {
             FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_src, TCA_FLOWER_KEY_IPV4_SRC);
             FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_dst, TCA_FLOWER_KEY_IPV4_DST);
@@ -2993,12 +3047,79 @@ nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
     return 0;
 }
 
+static void
+log_tc_flower_match(const char *msg,
+                    const struct tc_flower *a,
+                    const struct tc_flower *b)
+{
+    uint8_t key_a[sizeof(struct tc_flower_key)];
+    uint8_t key_b[sizeof(struct tc_flower_key)];
+    struct ds s = DS_EMPTY_INITIALIZER;
+
+    for (int i = 0; i < sizeof a->key; i++) {
+        uint8_t mask_a = ((uint8_t *) &a->mask)[i];
+        uint8_t mask_b = ((uint8_t *) &b->mask)[i];
+
+        key_a[i] = ((uint8_t *) &a->key)[i] & mask_a;
+        key_b[i] = ((uint8_t *) &b->key)[i] & mask_b;
+    }
+    ds_put_cstr(&s, "\nExpected Mask:\n");
+    ds_put_hex(&s, &a->mask, sizeof a->mask);
+    ds_put_cstr(&s, "\nReceived Mask:\n");
+    ds_put_hex(&s, &b->mask, sizeof b->mask);
+    ds_put_cstr(&s, "\nExpected Key:\n");
+    ds_put_hex(&s, &a->key, sizeof a->key);
+    ds_put_cstr(&s, "\nReceived Key:\n");
+    ds_put_hex(&s, &b->key, sizeof b->key);
+    ds_put_cstr(&s, "\nExpected Masked Key:\n");
+    ds_put_hex(&s, key_a, sizeof key_a);
+    ds_put_cstr(&s, "\nReceived Masked Key:\n");
+    ds_put_hex(&s, key_b, sizeof key_b);
+
+    if (a->action_count != b->action_count) {
+        /* If action count is not equal, we print all actions to see which
+         * ones are missing. */
+        const struct tc_action *action;
+        int i;
+
+        ds_put_cstr(&s, "\nExpected Actions:\n");
+        for (i = 0, action = a->actions; i < a->action_count; i++, action++) {
+            ds_put_cstr(&s, " - ");
+            ds_put_hex(&s, action, sizeof *action);
+            ds_put_cstr(&s, "\n");
+        }
+        ds_put_cstr(&s, "Received Actions:\n");
+        for (i = 0, action = b->actions; i < b->action_count; i++, action++) {
+            ds_put_cstr(&s, " - ");
+            ds_put_hex(&s, action, sizeof *action);
+            ds_put_cstr(&s, "\n");
+        }
+    } else {
+        /* Only dump the delta in actions. */
+        const struct tc_action *action_a = a->actions;
+        const struct tc_action *action_b = b->actions;
+
+        for (int i = 0; i < a->action_count; i++, action_a++, action_b++) {
+            if (memcmp(action_a, action_b, sizeof *action_a)) {
+                ds_put_format(&s,
+                              "\nAction %d mismatch:\n - Expected Action: ",
+                              i);
+                ds_put_hex(&s, action_a, sizeof *action_a);
+                ds_put_cstr(&s, "\n - Received Action: ");
+                ds_put_hex(&s, action_b, sizeof *action_b);
+            }
+        }
+    }
+    VLOG_DBG_RL(&error_rl, "%s%s", msg, ds_cstr(&s));
+    ds_destroy(&s);
+}
+
 static bool
 cmp_tc_flower_match_action(const struct tc_flower *a,
                            const struct tc_flower *b)
 {
     if (memcmp(&a->mask, &b->mask, sizeof a->mask)) {
-        VLOG_DBG_RL(&error_rl, "tc flower compare failed mask compare");
+        log_tc_flower_match("tc flower compare failed mask compare:", a, b);
         return false;
     }
 
@@ -3011,8 +3132,8 @@ cmp_tc_flower_match_action(const struct tc_flower *a,
         uint8_t key_b = ((uint8_t *)&b->key)[i] & mask;
 
         if (key_a != key_b) {
-            VLOG_DBG_RL(&error_rl, "tc flower compare failed key compare at "
-                        "%d", i);
+            log_tc_flower_match("tc flower compare failed masked key compare:",
+                                a, b);
             return false;
         }
     }
@@ -3022,14 +3143,15 @@ cmp_tc_flower_match_action(const struct tc_flower *a,
     const struct tc_action *action_b = b->actions;
 
     if (a->action_count != b->action_count) {
-        VLOG_DBG_RL(&error_rl, "tc flower compare failed action length check");
+        log_tc_flower_match("tc flower compare failed action length check",
+                            a, b);
         return false;
     }
 
     for (int i = 0; i < a->action_count; i++, action_a++, action_b++) {
         if (memcmp(action_a, action_b, sizeof *action_a)) {
-            VLOG_DBG_RL(&error_rl, "tc flower compare failed action compare "
-                        "for %d", i);
+            log_tc_flower_match("tc flower compare failed action compare",
+                                a, b);
             return false;
         }
     }
diff --git a/lib/tc.h b/lib/tc.h
index a147ca461d..d6cdddd169 100644
--- a/lib/tc.h
+++ b/lib/tc.h
@@ -256,11 +256,23 @@ struct tc_action {
             bool force;
             bool commit;
         } ct;
+
+        struct {
+            struct tc_flower_key key;
+            struct tc_flower_key mask;
+        } rewrite;
      };
 
      enum tc_action_type type;
 };
 
+/* assert that if we overflow with a masked write of uint32_t to the last byte
+ * of action.rewrite we overflow inside struct tc_action.
+ * shouldn't happen unless someone moves rewrite to the end of action */
+BUILD_ASSERT_DECL(offsetof(struct tc_action, rewrite)
+                  + MEMBER_SIZEOF(struct tc_action, rewrite)
+                  + sizeof(uint32_t) - 2 < sizeof(struct tc_action));
+
 enum tc_offloaded_state {
     TC_OFFLOADED_STATE_UNDEFINED,
     TC_OFFLOADED_STATE_IN_HW,
@@ -330,15 +342,10 @@ struct tc_flower {
     int action_count;
     struct tc_action actions[TCA_ACT_MAX_NUM];
 
-    struct ovs_flow_stats stats;
+    struct ovs_flow_stats stats_sw;
+    struct ovs_flow_stats stats_hw;
     uint64_t lastused;
 
-    struct {
-        bool rewrite;
-        struct tc_flower_key key;
-        struct tc_flower_key mask;
-    } rewrite;
-
     uint32_t csum_update_flags;
 
     bool tunnel;
@@ -352,13 +359,6 @@ struct tc_flower {
     enum tc_offload_policy tc_policy;
 };
 
-/* assert that if we overflow with a masked write of uint32_t to the last byte
- * of flower.rewrite we overflow inside struct flower.
- * shouldn't happen unless someone moves rewrite to the end of flower */
-BUILD_ASSERT_DECL(offsetof(struct tc_flower, rewrite)
-                  + MEMBER_SIZEOF(struct tc_flower, rewrite)
-                  + sizeof(uint32_t) - 2 < sizeof(struct tc_flower));
-
 int tc_replace_flower(struct tcf_id *id, struct tc_flower *flower);
 int tc_del_filter(struct tcf_id *id);
 int tc_get_flower(struct tcf_id *id, struct tc_flower *flower);
diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
index 58269d3b16..f9fee37939 100644
--- a/lib/tnl-ports.c
+++ b/lib/tnl-ports.c
@@ -259,14 +259,14 @@ ipdev_map_delete(struct ip_device *ip_dev, ovs_be16 tp_port, uint8_t nw_proto)
 void
 tnl_port_map_delete(odp_port_t port, const char type[])
 {
-    struct tnl_port *p, *next;
+    struct tnl_port *p;
     struct ip_device *ip_dev;
     uint8_t nw_proto;
 
     nw_proto = tnl_type_to_nw_proto(type);
 
     ovs_mutex_lock(&mutex);
-    LIST_FOR_EACH_SAFE(p, next, node, &port_list) {
+    LIST_FOR_EACH_SAFE (p, node, &port_list) {
         if (p->port == port && p->nw_proto == nw_proto &&
                     ovs_refcount_unref_relaxed(&p->ref_cnt) == 1) {
             ovs_list_remove(&p->node);
@@ -444,11 +444,11 @@ delete_ipdev(struct ip_device *ip_dev)
 void
 tnl_port_map_insert_ipdev(const char dev_name[])
 {
-    struct ip_device *ip_dev, *next;
+    struct ip_device *ip_dev;
 
     ovs_mutex_lock(&mutex);
 
-    LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
+    LIST_FOR_EACH_SAFE (ip_dev, node, &addr_list) {
         if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
             if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
                 goto out;
@@ -466,10 +466,10 @@ out:
 void
 tnl_port_map_delete_ipdev(const char dev_name[])
 {
-    struct ip_device *ip_dev, *next;
+    struct ip_device *ip_dev;
 
     ovs_mutex_lock(&mutex);
-    LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
+    LIST_FOR_EACH_SAFE (ip_dev, node, &addr_list) {
         if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
             delete_ipdev(ip_dev);
         }
@@ -480,10 +480,10 @@ tnl_port_map_delete_ipdev(const char dev_name[])
 void
 tnl_port_map_run(void)
 {
-    struct ip_device *ip_dev, *next;
+    struct ip_device *ip_dev;
 
     ovs_mutex_lock(&mutex);
-    LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
+    LIST_FOR_EACH_SAFE (ip_dev, node, &addr_list) {
         char dev_name[IFNAMSIZ];
 
         if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
diff --git a/lib/unixctl.c b/lib/unixctl.c
index 69aed6722c..103357ee91 100644
--- a/lib/unixctl.c
+++ b/lib/unixctl.c
@@ -390,8 +390,8 @@ unixctl_server_run(struct unixctl_server *server)
         }
     }
 
-    struct unixctl_conn *conn, *next;
-    LIST_FOR_EACH_SAFE (conn, next, node, &server->conns) {
+    struct unixctl_conn *conn;
+    LIST_FOR_EACH_SAFE (conn, node, &server->conns) {
         int error = run_connection(conn);
         if (error && error != EAGAIN) {
             kill_connection(conn);
@@ -422,9 +422,9 @@ void
 unixctl_server_destroy(struct unixctl_server *server)
 {
     if (server) {
-        struct unixctl_conn *conn, *next;
+        struct unixctl_conn *conn;
 
-        LIST_FOR_EACH_SAFE (conn, next, node, &server->conns) {
+        LIST_FOR_EACH_SAFE (conn, node, &server->conns) {
             kill_connection(conn);
         }
 
diff --git a/lib/vconn.c b/lib/vconn.c
index 7415e6291f..b556762277 100644
--- a/lib/vconn.c
+++ b/lib/vconn.c
@@ -960,8 +960,8 @@ vconn_transact_multipart(struct vconn *vconn,
     ovs_list_init(replies);
 
     /* Send all the requests. */
-    struct ofpbuf *b, *next;
-    LIST_FOR_EACH_SAFE (b, next, list_node, requests) {
+    struct ofpbuf *b;
+    LIST_FOR_EACH_SAFE (b, list_node, requests) {
         ovs_list_remove(&b->list_node);
         int error = vconn_send_block(vconn, b);
         if (error) {
diff --git a/ofproto/bond.c b/ofproto/bond.c
index cdfdf0b9d8..845f69e21d 100644
--- a/ofproto/bond.c
+++ b/ofproto/bond.c
@@ -338,7 +338,7 @@ static void
 update_recirc_rules__(struct bond *bond)
 {
     struct match match;
-    struct bond_pr_rule_op *pr_op, *next_op;
+    struct bond_pr_rule_op *pr_op;
     uint64_t ofpacts_stub[128 / 8];
     struct ofpbuf ofpacts;
     int i;
@@ -372,7 +372,7 @@ update_recirc_rules__(struct bond *bond)
 
     ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
 
-    HMAP_FOR_EACH_SAFE(pr_op, next_op, hmap_node, &bond->pr_rule_ops) {
+    HMAP_FOR_EACH_SAFE (pr_op, hmap_node, &bond->pr_rule_ops) {
         int error;
         switch (pr_op->op) {
         case ADD:
@@ -1258,7 +1258,7 @@ insert_bal(struct ovs_list *bals, struct bond_member *member)
             break;
         }
     }
-    ovs_list_insert(&pos->bal_node, &member->bal_node);
+    ovs_list_insert(pos ? &pos->bal_node : bals, &member->bal_node);
 }
 
 /* Removes 'member' from its current list and then inserts it into 'bals' so
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index fa8f6cd0e8..172a58cfb7 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -310,8 +310,8 @@ connmgr_destroy(struct connmgr *mgr)
         return;
     }
 
-    struct ofservice *ofservice, *next_ofservice;
-    HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, hmap_node, &mgr->services) {
+    struct ofservice *ofservice;
+    HMAP_FOR_EACH_SAFE (ofservice, hmap_node, &mgr->services) {
         ofservice_destroy(ofservice);
     }
     hmap_destroy(&mgr->services);
@@ -351,8 +351,8 @@ connmgr_run(struct connmgr *mgr,
         }
     }
 
-    struct ofconn *ofconn, *next_ofconn;
-    LIST_FOR_EACH_SAFE (ofconn, next_ofconn, connmgr_node, &mgr->conns) {
+    struct ofconn *ofconn;
+    LIST_FOR_EACH_SAFE (ofconn, connmgr_node, &mgr->conns) {
         ofconn_run(ofconn, handle_openflow);
     }
     ofmonitor_run(mgr);
@@ -592,8 +592,8 @@ connmgr_set_controllers(struct connmgr *mgr, struct shash *controllers)
 
     /* Delete services that are no longer configured.
      * Update configuration of all now-existing services. */
-    struct ofservice *ofservice, *next_ofservice;
-    HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, hmap_node, &mgr->services) {
+    struct ofservice *ofservice;
+    HMAP_FOR_EACH_SAFE (ofservice, hmap_node, &mgr->services) {
         const char *target = ofservice->target;
         struct ofproto_controller *c = shash_find_data(controllers, target);
         if (!c) {
@@ -1137,9 +1137,9 @@ ofconn_remove_bundle(struct ofconn *ofconn, struct ofp_bundle *bundle)
 static void
 bundle_remove_all(struct ofconn *ofconn)
 {
-    struct ofp_bundle *b, *next;
+    struct ofp_bundle *b;
 
-    HMAP_FOR_EACH_SAFE (b, next, node, &ofconn->bundles) {
+    HMAP_FOR_EACH_SAFE (b, node, &ofconn->bundles) {
         ofp_bundle_remove__(ofconn, b);
     }
 }
@@ -1149,8 +1149,8 @@ bundle_remove_expired(struct ofconn *ofconn, long long int now)
 {
     long long int limit = now - bundle_idle_timeout;
 
-    struct ofp_bundle *b, *next;
-    HMAP_FOR_EACH_SAFE (b, next, node, &ofconn->bundles) {
+    struct ofp_bundle *b;
+    HMAP_FOR_EACH_SAFE (b, node, &ofconn->bundles) {
         if (b->used <= limit) {
             ofconn_send_error(ofconn, b->msg, OFPERR_OFPBFC_TIMEOUT);
             ofp_bundle_remove__(ofconn, b);
@@ -1247,8 +1247,8 @@ ofconn_destroy(struct ofconn *ofconn)
 
     free(ofconn->async_cfg);
 
-    struct ofmonitor *monitor, *next_monitor;
-    HMAP_FOR_EACH_SAFE (monitor, next_monitor, ofconn_node,
+    struct ofmonitor *monitor;
+    HMAP_FOR_EACH_SAFE (monitor, ofconn_node,
                         &ofconn->monitors) {
         ofmonitor_destroy(monitor);
     }
@@ -1953,8 +1953,8 @@ static void
 ofservice_close_all(struct ofservice *ofservice)
     OVS_REQUIRES(ofproto_mutex)
 {
-    struct ofconn *ofconn, *next;
-    LIST_FOR_EACH_SAFE (ofconn, next, ofservice_node, &ofservice->conns) {
+    struct ofconn *ofconn;
+    LIST_FOR_EACH_SAFE (ofconn, ofservice_node, &ofservice->conns) {
         ofconn_destroy(ofconn);
     }
 }
diff --git a/ofproto/in-band.c b/ofproto/in-band.c
index 82d8dfa147..3992251f5f 100644
--- a/ofproto/in-band.c
+++ b/ofproto/in-band.c
@@ -377,7 +377,7 @@ in_band_run(struct in_band *ib)
     uint64_t ofpacts_stub[128 / 8];
     struct ofpbuf ofpacts;
 
-    struct in_band_rule *rule, *next;
+    struct in_band_rule *rule;
 
     ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
 
@@ -391,7 +391,7 @@ in_band_run(struct in_band *ib)
 
     update_rules(ib);
 
-    HMAP_FOR_EACH_SAFE (rule, next, hmap_node, &ib->rules) {
+    HMAP_FOR_EACH_SAFE (rule, hmap_node, &ib->rules) {
         switch (rule->op) {
         case ADD:
             ofproto_add_flow(ib->ofproto, &rule->match, rule->priority,
diff --git a/ofproto/netflow.c b/ofproto/netflow.c
index ed58de17de..aad9f9c77a 100644
--- a/ofproto/netflow.c
+++ b/ofproto/netflow.c
@@ -299,7 +299,7 @@ static void
 netflow_run__(struct netflow *nf) OVS_REQUIRES(mutex)
 {
     long long int now = time_msec();
-    struct netflow_flow *nf_flow, *next;
+    struct netflow_flow *nf_flow;
 
     if (nf->packet.size) {
         collectors_send(nf->collectors, nf->packet.data, nf->packet.size);
@@ -312,7 +312,7 @@ netflow_run__(struct netflow *nf) OVS_REQUIRES(mutex)
 
     nf->next_timeout = now + 1000;
 
-    HMAP_FOR_EACH_SAFE (nf_flow, next, hmap_node, &nf->flows) {
+    HMAP_FOR_EACH_SAFE (nf_flow, hmap_node, &nf->flows) {
         if (now > nf_flow->last_expired + nf->active_timeout) {
             bool idle = nf_flow->used < nf_flow->last_expired;
             netflow_expire__(nf, nf_flow);
@@ -416,8 +416,8 @@ netflow_unref(struct netflow *nf)
         collectors_destroy(nf->collectors);
         ofpbuf_uninit(&nf->packet);
 
-        struct netflow_flow *nf_flow, *next;
-        HMAP_FOR_EACH_SAFE (nf_flow, next, hmap_node, &nf->flows) {
+        struct netflow_flow *nf_flow;
+        HMAP_FOR_EACH_SAFE (nf_flow, hmap_node, &nf->flows) {
             hmap_remove(&nf->flows, &nf_flow->hmap_node);
             free(nf_flow);
         }
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 9280e008ea..fc927fe866 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -1078,7 +1078,7 @@ dpif_ipfix_set_options(
 {
     int i;
     struct ofproto_ipfix_flow_exporter_options *options;
-    struct dpif_ipfix_flow_exporter_map_node *node, *next;
+    struct dpif_ipfix_flow_exporter_map_node *node;
 
     ovs_mutex_lock(&mutex);
     dpif_ipfix_bridge_exporter_set_options(&di->bridge_exporter,
@@ -1103,7 +1103,7 @@ dpif_ipfix_set_options(
     }
 
     /* Remove dropped flow exporters, if any needs to be removed. */
-    HMAP_FOR_EACH_SAFE (node, next, node, &di->flow_exporter_map) {
+    HMAP_FOR_EACH_SAFE (node, node, &di->flow_exporter_map) {
         /* This is slow but doesn't take any extra memory, and
          * this table is not supposed to contain many rows anyway. */
         options = (struct ofproto_ipfix_flow_exporter_options *)
@@ -1215,7 +1215,7 @@ static void
 dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex)
 {
     struct dpif_ipfix_flow_exporter_map_node *exp_node;
-    struct dpif_ipfix_port *dip, *next;
+    struct dpif_ipfix_port *dip;
 
     dpif_ipfix_bridge_exporter_clear(&di->bridge_exporter);
 
@@ -1224,7 +1224,7 @@ dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex)
         free(exp_node);
     }
 
-    HMAP_FOR_EACH_SAFE (dip, next, hmap_node, &di->ports) {
+    HMAP_FOR_EACH_SAFE (dip, hmap_node, &di->ports) {
         dpif_ipfix_del_port__(di, dip);
     }
 }
@@ -2799,7 +2799,7 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
                         bool forced_end, const uint64_t export_time_usec,
                         const uint32_t export_time_sec)
 {
-    struct ipfix_flow_cache_entry *entry, *next_entry;
+    struct ipfix_flow_cache_entry *entry;
     uint64_t max_flow_start_timestamp_usec;
     bool template_msg_sent = false;
     enum ipfix_flow_end_reason flow_end_reason;
@@ -2811,7 +2811,7 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
     max_flow_start_timestamp_usec = export_time_usec -
         1000000LL * exporter->cache_active_timeout;
 
-    LIST_FOR_EACH_SAFE (entry, next_entry, cache_flow_start_timestamp_list_node,
+    LIST_FOR_EACH_SAFE (entry, cache_flow_start_timestamp_list_node,
                         &exporter->cache_flow_start_timestamp_list) {
         if (forced_end) {
             flow_end_reason = FORCED_END;
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 30e7caf54a..e8e1de920b 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -468,7 +468,8 @@ sflow_choose_agent_address(const char *agent_device,
     const char *target;
     SSET_FOR_EACH (target, targets) {
         struct sockaddr_storage ss;
-        if (inet_parse_active(target, SFL_DEFAULT_COLLECTOR_PORT, &ss, true)) {
+        if (inet_parse_active(target, SFL_DEFAULT_COLLECTOR_PORT,
+                              &ss, true, NULL)) {
             /* sFlow only supports target in default routing table with
              * packet mark zero.
              */
@@ -590,10 +591,10 @@ void
 dpif_sflow_unref(struct dpif_sflow *ds) OVS_EXCLUDED(mutex)
 {
     if (ds && ovs_refcount_unref_relaxed(&ds->ref_cnt) == 1) {
-        struct dpif_sflow_port *dsp, *next;
+        struct dpif_sflow_port *dsp;
 
         dpif_sflow_clear(ds);
-        HMAP_FOR_EACH_SAFE (dsp, next, hmap_node, &ds->ports) {
+        HMAP_FOR_EACH_SAFE (dsp, hmap_node, &ds->ports) {
             dpif_sflow_del_port__(ds, dsp);
         }
         hmap_destroy(&ds->ports);
diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
index 78a54c715d..109940ad2a 100644
--- a/ofproto/ofproto-dpif-trace.c
+++ b/ofproto/ofproto-dpif-trace.c
@@ -65,8 +65,8 @@ static void
 oftrace_node_list_destroy(struct ovs_list *nodes)
 {
     if (nodes) {
-        struct oftrace_node *node, *next;
-        LIST_FOR_EACH_SAFE (node, next, node, nodes) {
+        struct oftrace_node *node;
+        LIST_FOR_EACH_SAFE (node, node, nodes) {
             ovs_list_remove(&node->node);
             oftrace_node_destroy(node);
         }
diff --git a/ofproto/ofproto-dpif-xlate-cache.c b/ofproto/ofproto-dpif-xlate-cache.c
index dcc91cb380..9224ee2e6d 100644
--- a/ofproto/ofproto-dpif-xlate-cache.c
+++ b/ofproto/ofproto-dpif-xlate-cache.c
@@ -209,6 +209,7 @@ xlate_cache_clear_entry(struct xc_entry *entry)
 {
     switch (entry->type) {
     case XC_TABLE:
+        ofproto_unref(&(entry->table.ofproto->up));
         break;
     case XC_RULE:
         ofproto_rule_unref(&entry->rule->up);
@@ -231,6 +232,7 @@ xlate_cache_clear_entry(struct xc_entry *entry)
         free(entry->learn.ofm);
         break;
     case XC_NORMAL:
+        ofproto_unref(&(entry->normal.ofproto->up));
         break;
     case XC_FIN_TIMEOUT:
         /* 'u.fin.rule' is always already held as a XC_RULE, which
diff --git a/ofproto/ofproto-dpif-xlate-cache.h b/ofproto/ofproto-dpif-xlate-cache.h
index 114aff8ea3..0fc6d2ea60 100644
--- a/ofproto/ofproto-dpif-xlate-cache.h
+++ b/ofproto/ofproto-dpif-xlate-cache.h
@@ -61,9 +61,8 @@ enum xc_type {
  * that a flow relates to, although they may be used for other effects as well
  * (for instance, refreshing hard timeouts for learned flows).
  *
- * An explicit reference is taken to all pointers other than the ones for
- * struct ofproto_dpif.  ofproto_dpif pointers are explicitly protected by
- * destroying all xlate caches before the ofproto is destroyed. */
+ * An explicit reference is taken to all pointers.
+ */
 struct xc_entry {
     enum xc_type type;
     union {
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 578cbfe581..17f7e2883f 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -865,7 +865,7 @@ xlate_xbridge_init(struct xlate_cfg *xcfg, struct xbridge *xbridge)
     ovs_list_init(&xbridge->xbundles);
     hmap_init(&xbridge->xports);
     hmap_insert(&xcfg->xbridges, &xbridge->hmap_node,
-                hash_pointer(xbridge->ofproto, 0));
+                uuid_hash(&xbridge->ofproto->uuid));
 }
 
 static void
@@ -1222,13 +1222,13 @@ xlate_txn_start(void)
 static void
 xlate_xcfg_free(struct xlate_cfg *xcfg)
 {
-    struct xbridge *xbridge, *next_xbridge;
+    struct xbridge *xbridge;
 
     if (!xcfg) {
         return;
     }
 
-    HMAP_FOR_EACH_SAFE (xbridge, next_xbridge, hmap_node, &xcfg->xbridges) {
+    HMAP_FOR_EACH_SAFE (xbridge, hmap_node, &xcfg->xbridges) {
         xlate_xbridge_remove(xcfg, xbridge);
     }
 
@@ -1282,18 +1282,18 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
 static void
 xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
 {
-    struct xbundle *xbundle, *next_xbundle;
-    struct xport *xport, *next_xport;
+    struct xbundle *xbundle;
+    struct xport *xport;
 
     if (!xbridge) {
         return;
     }
 
-    HMAP_FOR_EACH_SAFE (xport, next_xport, ofp_node, &xbridge->xports) {
+    HMAP_FOR_EACH_SAFE (xport, ofp_node, &xbridge->xports) {
         xlate_xport_remove(xcfg, xport);
     }
 
-    LIST_FOR_EACH_SAFE (xbundle, next_xbundle, list_node, &xbridge->xbundles) {
+    LIST_FOR_EACH_SAFE (xbundle, list_node, &xbridge->xbundles) {
         xlate_xbundle_remove(xcfg, xbundle);
     }
 
@@ -1639,7 +1639,7 @@ xbridge_lookup(struct xlate_cfg *xcfg, const struct ofproto_dpif *ofproto)
 
     xbridges = &xcfg->xbridges;
 
-    HMAP_FOR_EACH_IN_BUCKET (xbridge, hmap_node, hash_pointer(ofproto, 0),
+    HMAP_FOR_EACH_IN_BUCKET (xbridge, hmap_node, uuid_hash(&ofproto->uuid),
                              xbridges) {
         if (xbridge->ofproto == ofproto) {
             return xbridge;
@@ -1661,6 +1661,23 @@ xbridge_lookup_by_uuid(struct xlate_cfg *xcfg, const struct uuid *uuid)
     return NULL;
 }
 
+struct ofproto_dpif *
+xlate_ofproto_lookup(const struct uuid *uuid)
+{
+    struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
+    struct xbridge *xbridge;
+
+    if (!xcfg) {
+        return NULL;
+    }
+
+    xbridge = xbridge_lookup_by_uuid(xcfg, uuid);
+    if (xbridge != NULL) {
+        return xbridge->ofproto;
+    }
+    return NULL;
+}
+
 static struct xbundle *
 xbundle_lookup(struct xlate_cfg *xcfg, const struct ofbundle *ofbundle)
 {
@@ -3015,7 +3032,7 @@ xlate_normal(struct xlate_ctx *ctx)
     bool is_grat_arp = is_gratuitous_arp(flow, wc);
     if (ctx->xin->allow_side_effects
         && flow->packet_type == htonl(PT_ETH)
-        && in_port->pt_mode != NETDEV_PT_LEGACY_L3
+        && in_port && in_port->pt_mode != NETDEV_PT_LEGACY_L3
     ) {
         update_learning_table(ctx, in_xbundle, flow->dl_src, vlan,
                               is_grat_arp);
@@ -3024,12 +3041,14 @@ xlate_normal(struct xlate_ctx *ctx)
         struct xc_entry *entry;
 
         /* Save just enough info to update mac learning table later. */
-        entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NORMAL);
-        entry->normal.ofproto = ctx->xbridge->ofproto;
-        entry->normal.in_port = flow->in_port.ofp_port;
-        entry->normal.dl_src = flow->dl_src;
-        entry->normal.vlan = vlan;
-        entry->normal.is_gratuitous_arp = is_grat_arp;
+        if (ofproto_try_ref(&ctx->xbridge->ofproto->up)) {
+            entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NORMAL);
+            entry->normal.ofproto = ctx->xbridge->ofproto;
+            entry->normal.in_port = flow->in_port.ofp_port;
+            entry->normal.dl_src = flow->dl_src;
+            entry->normal.vlan = vlan;
+            entry->normal.is_gratuitous_arp = is_grat_arp;
+        }
     }
 
     /* Determine output bundle. */
@@ -3523,6 +3542,9 @@ propagate_tunnel_data_to_flow__(struct flow *dst_flow,
     dst_flow->dl_dst = dmac;
     dst_flow->dl_src = smac;
 
+    /* Clear VLAN entries which do not apply for tunnel flows. */
+    memset(dst_flow->vlans, 0, sizeof dst_flow->vlans);
+
     dst_flow->packet_type = htonl(PT_ETH);
     dst_flow->nw_dst = src_flow->tunnel.ip_dst;
     dst_flow->nw_src = src_flow->tunnel.ip_src;
@@ -4176,6 +4198,10 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
         if (xport->pt_mode == NETDEV_PT_LEGACY_L3) {
             flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE,
                                                ntohs(flow->dl_type));
+            if (ctx->pending_encap) {
+                /* The Ethernet header was not actually added yet. */
+                ctx->pending_encap = false;
+            }
         }
     }
 
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 851088d794..2ba90e999c 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -176,6 +176,7 @@ void xlate_ofproto_set(struct ofproto_dpif *, const char *name, struct dpif *,
                        bool forward_bpdu, bool has_in_band,
                        const struct dpif_backer_support *support);
 void xlate_remove_ofproto(struct ofproto_dpif *);
+struct ofproto_dpif *xlate_ofproto_lookup(const struct uuid *uuid);
 
 void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *,
                       const char *name, enum port_vlan_mode,
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 8143dd965f..6601f23464 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -215,10 +215,6 @@ struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
 static struct hmap all_ofproto_dpifs_by_name =
                           HMAP_INITIALIZER(&all_ofproto_dpifs_by_name);
 
-/* All existing ofproto_dpif instances, indexed by ->uuid. */
-static struct hmap all_ofproto_dpifs_by_uuid =
-                          HMAP_INITIALIZER(&all_ofproto_dpifs_by_uuid);
-
 static bool ofproto_use_tnl_push_pop = true;
 static void ofproto_unixctl_init(void);
 static void ct_zone_config_init(struct dpif_backer *backer);
@@ -1663,7 +1659,7 @@ static int
 construct(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct shash_node *node, *next;
+    struct shash_node *node;
     int error;
 
     /* Tunnel module can get used right after the udpif threads are running. */
@@ -1701,7 +1697,7 @@ construct(struct ofproto *ofproto_)
     ofproto->ams_seqno = seq_read(ofproto->ams_seq);
 
 
-    SHASH_FOR_EACH_SAFE (node, next, &init_ofp_ports) {
+    SHASH_FOR_EACH_SAFE (node, &init_ofp_ports) {
         struct iface_hint *iface_hint = node->data;
 
         if (!strcmp(iface_hint->br_name, ofproto->up.name)) {
@@ -1720,9 +1716,6 @@ construct(struct ofproto *ofproto_)
     hmap_insert(&all_ofproto_dpifs_by_name,
                 &ofproto->all_ofproto_dpifs_by_name_node,
                 hash_string(ofproto->up.name, 0));
-    hmap_insert(&all_ofproto_dpifs_by_uuid,
-                &ofproto->all_ofproto_dpifs_by_uuid_node,
-                uuid_hash(&ofproto->uuid));
     memset(&ofproto->stats, 0, sizeof ofproto->stats);
 
     ofproto_init_tables(ofproto_, N_TABLES);
@@ -1820,8 +1813,6 @@ destruct(struct ofproto *ofproto_, bool del)
 
     hmap_remove(&all_ofproto_dpifs_by_name,
                 &ofproto->all_ofproto_dpifs_by_name_node);
-    hmap_remove(&all_ofproto_dpifs_by_uuid,
-                &ofproto->all_ofproto_dpifs_by_uuid_node);
 
     OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
         CLS_FOR_EACH (rule, up.cr, &table->cls) {
@@ -1945,7 +1936,7 @@ run(struct ofproto *ofproto_)
 
     new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif));
     if (ofproto->dump_seq != new_dump_seq) {
-        struct rule *rule, *next_rule;
+        struct rule *rule;
         long long now = time_msec();
 
         /* We know stats are relatively fresh, so now is a good time to do some
@@ -1955,7 +1946,7 @@ run(struct ofproto *ofproto_)
         /* Expire OpenFlow flows whose idle_timeout or hard_timeout
          * has passed. */
         ovs_mutex_lock(&ofproto_mutex);
-        LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
+        LIST_FOR_EACH_SAFE (rule, expirable,
                             &ofproto->up.expirable) {
             rule_expire(rule_dpif_cast(rule), now);
         }
@@ -2371,6 +2362,12 @@ set_ipfix(
             dpif_ipfix_unref(di);
             ofproto->ipfix = NULL;
         }
+
+        /* TODO: need to consider ipfix option changes more than
+         * enable/disable */
+        if (new_di || !ofproto->ipfix) {
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
+        }
     }
 
     return 0;
@@ -3106,11 +3103,11 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos)
 {
     struct ofproto_dpif *ofproto = bundle->ofproto;
     struct mac_learning *ml = ofproto->ml;
-    struct mac_entry *mac, *next_mac;
+    struct mac_entry *mac;
 
     ofproto->backer->need_revalidate = REV_RECONFIGURE;
     ovs_rwlock_wrlock(&ml->rwlock);
-    LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
+    LIST_FOR_EACH_SAFE (mac, lru_node, &ml->lrus) {
         if (mac_entry_get_port(ml, mac) == bundle) {
             if (all_ofprotos) {
                 struct ofproto_dpif *o;
@@ -3141,13 +3138,13 @@ bundle_move(struct ofbundle *old, struct ofbundle *new)
 {
     struct ofproto_dpif *ofproto = old->ofproto;
     struct mac_learning *ml = ofproto->ml;
-    struct mac_entry *mac, *next_mac;
+    struct mac_entry *mac;
 
     ovs_assert(new->ofproto == old->ofproto);
 
     ofproto->backer->need_revalidate = REV_RECONFIGURE;
     ovs_rwlock_wrlock(&ml->rwlock);
-    LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
+    LIST_FOR_EACH_SAFE (mac, lru_node, &ml->lrus) {
         if (mac_entry_get_port(ml, mac) == old) {
             mac_entry_set_port(ml, mac, new);
         }
@@ -3244,7 +3241,7 @@ static void
 bundle_destroy(struct ofbundle *bundle)
 {
     struct ofproto_dpif *ofproto;
-    struct ofport_dpif *port, *next_port;
+    struct ofport_dpif *port;
 
     if (!bundle) {
         return;
@@ -3257,7 +3254,7 @@ bundle_destroy(struct ofbundle *bundle)
     xlate_bundle_remove(bundle);
     xlate_txn_commit();
 
-    LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) {
+    LIST_FOR_EACH_SAFE (port, bundle_node, &bundle->ports) {
         bundle_del_port(port);
     }
 
@@ -3347,9 +3344,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         }
     }
     if (!ok || ovs_list_size(&bundle->ports) != s->n_members) {
-        struct ofport_dpif *next_port;
-
-        LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) {
+        LIST_FOR_EACH_SAFE (port, bundle_node, &bundle->ports) {
             for (i = 0; i < s->n_members; i++) {
                 if (s->members[i] == port->up.ofp_port) {
                     goto found;
@@ -3963,6 +3958,10 @@ port_add(struct ofproto *ofproto_, struct netdev *netdev)
             simap_put(&ofproto->backer->tnl_backers,
                       dp_port_name, odp_to_u32(port_no));
         }
+    } else {
+        struct dpif *dpif = ofproto->backer->dpif;
+        const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif));
+        netdev_set_dpif_type(netdev, dpif_type_str);
     }
 
     if (netdev_get_tunnel_config(netdev)) {
@@ -4471,12 +4470,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto,
                 atomic_add_relaxed(&tbl->n_matched, stats->n_packets, &orig);
             }
             if (xcache) {
-                struct xc_entry *entry;
+                if (ofproto_try_ref(&ofproto->up)) {
+                    struct xc_entry *entry;
 
-                entry = xlate_cache_add_entry(xcache, XC_TABLE);
-                entry->table.ofproto = ofproto;
-                entry->table.id = *table_id;
-                entry->table.match = true;
+                    entry = xlate_cache_add_entry(xcache, XC_TABLE);
+                    entry->table.ofproto = ofproto;
+                    entry->table.id = *table_id;
+                    entry->table.match = true;
+                }
             }
             return rule;
         }
@@ -4507,12 +4508,14 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto,
                                stats->n_packets, &orig);
         }
         if (xcache) {
-            struct xc_entry *entry;
+            if (ofproto_try_ref(&ofproto->up)) {
+                struct xc_entry *entry;
 
-            entry = xlate_cache_add_entry(xcache, XC_TABLE);
-            entry->table.ofproto = ofproto;
-            entry->table.id = next_id;
-            entry->table.match = (rule != NULL);
+                entry = xlate_cache_add_entry(xcache, XC_TABLE);
+                entry->table.ofproto = ofproto;
+                entry->table.id = next_id;
+                entry->table.match = (rule != NULL);
+            }
         }
         if (rule) {
             goto out;   /* Match. */
@@ -5550,9 +5553,9 @@ ct_zone_timeout_policy_sweep(struct dpif_backer *backer)
 {
     if (!ovs_list_is_empty(&backer->ct_tp_kill_list)
         && time_msec() >= timeout_policy_cleanup_timer) {
-        struct ct_timeout_policy *ct_tp, *next;
+        struct ct_timeout_policy *ct_tp;
 
-        LIST_FOR_EACH_SAFE (ct_tp, next, list_node, &backer->ct_tp_kill_list) {
+        LIST_FOR_EACH_SAFE (ct_tp, list_node, &backer->ct_tp_kill_list) {
             if (!ct_dpif_del_timeout_policy(backer->dpif, ct_tp->tp_id)) {
                 ovs_list_remove(&ct_tp->list_node);
                 ct_timeout_policy_destroy(ct_tp, backer->tp_ids);
@@ -5818,15 +5821,7 @@ ofproto_dpif_lookup_by_name(const char *name)
 struct ofproto_dpif *
 ofproto_dpif_lookup_by_uuid(const struct uuid *uuid)
 {
-    struct ofproto_dpif *ofproto;
-
-    HMAP_FOR_EACH_WITH_HASH (ofproto, all_ofproto_dpifs_by_uuid_node,
-                             uuid_hash(uuid), &all_ofproto_dpifs_by_uuid) {
-        if (uuid_equals(&ofproto->uuid, uuid)) {
-            return ofproto;
-        }
-    }
-    return NULL;
+    return xlate_ofproto_lookup(uuid);
 }
 
 static void
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 14b909973d..47e96e62e1 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -143,6 +143,8 @@ struct ofproto {
     /* Variable length mf_field mapping. Stores all configured variable length
      * meta-flow fields (struct mf_field) in a switch. */
     struct vl_mff_map vl_mff_map;
+    /* refcount to this ofproto, held by rule/group/xlate_caches */
+    struct ovs_refcount refcount;
 };
 
 void ofproto_init_tables(struct ofproto *, int n_tables);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 56aeac7209..2ed1078007 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -549,6 +549,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
 
     ovs_mutex_init(&ofproto->vl_mff_map.mutex);
     cmap_init(&ofproto->vl_mff_map.cmap);
+    ovs_refcount_init(&ofproto->refcount);
 
     error = ofproto->ofproto_class->construct(ofproto);
     if (error) {
@@ -1695,9 +1696,33 @@ ofproto_destroy__(struct ofproto *ofproto)
     ofproto->ofproto_class->dealloc(ofproto);
 }
 
-/* Destroying rules is doubly deferred, must have 'ofproto' around for them.
- * - 1st we defer the removal of the rules from the classifier
- * - 2nd we defer the actual destruction of the rules. */
+/*
+ * Rule destruction requires ofproto to remain accessible.
+ * Depending on the rule destruction call (shown in below), it can take several
+ * RCU grace periods before the ofproto reference is not needed anymore.
+ * The ofproto destruction callback is thus protected by a refcount,
+ * and such destruction is itself deferred.
+ *
+ * remove_rules_postponed (one grace period)
+ *       -> remove_rule_rcu
+ *           -> remove_rule_rcu__
+ *               -> ofproto_rule_unref -> ref count != 1
+ *                   -> ... more grace periods.
+ *                   -> rule_destroy_cb (> 2 grace periods)
+ *                       -> free
+ *
+ * NOTE: The original ofproto destruction is only deferred by two grace
+ * periods to keep ofproto accessible. By using refcount together the
+ * destruction can be deferred for longer time. Now ofproto has 3 states:
+ *
+ * state 1: alive, with refcount >= 1
+ * state 2: dying, with refcount == 0, however pointer is valid
+ * state 3: died, memory freed, pointer might be dangling.
+ *
+ * We only need to add refcount to certain objects whose destruction can
+ * take several RCU grace periods (rule, group, xlate_cache). Other
+ * references to ofproto must be cleared before the 2 RCU grace periods.
+ */
 static void
 ofproto_destroy_defer__(struct ofproto *ofproto)
     OVS_EXCLUDED(ofproto_mutex)
@@ -1705,11 +1730,31 @@ ofproto_destroy_defer__(struct ofproto *ofproto)
     ovsrcu_postpone(ofproto_destroy__, ofproto);
 }
 
+void
+ofproto_ref(struct ofproto *ofproto)
+{
+    ovs_refcount_ref(&ofproto->refcount);
+}
+
+bool
+ofproto_try_ref(struct ofproto *ofproto)
+{
+    return ovs_refcount_try_ref_rcu(&ofproto->refcount);
+}
+
+void
+ofproto_unref(struct ofproto *ofproto)
+{
+    if (ofproto && ovs_refcount_unref(&ofproto->refcount) == 1) {
+        ovsrcu_postpone(ofproto_destroy_defer__, ofproto);
+    }
+}
+
 void
 ofproto_destroy(struct ofproto *p, bool del)
     OVS_EXCLUDED(ofproto_mutex)
 {
-    struct ofport *ofport, *next_ofport;
+    struct ofport *ofport;
     struct ofport_usage *usage;
 
     if (!p) {
@@ -1717,7 +1762,7 @@ ofproto_destroy(struct ofproto *p, bool del)
     }
 
     ofproto_flush__(p, del);
-    HMAP_FOR_EACH_SAFE (ofport, next_ofport, hmap_node, &p->ports) {
+    HMAP_FOR_EACH_SAFE (ofport, hmap_node, &p->ports) {
         ofport_destroy(ofport, del);
     }
 
@@ -1736,8 +1781,7 @@ ofproto_destroy(struct ofproto *p, bool del)
     p->connmgr = NULL;
     ovs_mutex_unlock(&ofproto_mutex);
 
-    /* Destroying rules is deferred, must have 'ofproto' around for them. */
-    ovsrcu_postpone(ofproto_destroy_defer__, p);
+    ofproto_unref(p);
 }
 
 /* Destroys the datapath with the respective 'name' and 'type'.  With the Linux
@@ -2782,7 +2826,7 @@ init_ports(struct ofproto *p)
 {
     struct ofproto_port_dump dump;
     struct ofproto_port ofproto_port;
-    struct shash_node *node, *next;
+    struct shash_node *node;
 
     OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) {
         const char *name = ofproto_port.name;
@@ -2813,7 +2857,7 @@ init_ports(struct ofproto *p)
         }
     }
 
-    SHASH_FOR_EACH_SAFE(node, next, &init_ofp_ports) {
+    SHASH_FOR_EACH_SAFE (node, &init_ofp_ports) {
         struct iface_hint *iface_hint = node->data;
 
         if (!strcmp(iface_hint->br_name, p->name)) {
@@ -2929,6 +2973,9 @@ ofproto_rule_destroy__(struct rule *rule)
     cls_rule_destroy(CONST_CAST(struct cls_rule *, &rule->cr));
     rule_actions_destroy(rule_get_actions(rule));
     ovs_mutex_destroy(&rule->mutex);
+    /* ofproto_unref() must be called first. It is possible because ofproto
+     * destruction is deferred by an RCU grace period. */
+    ofproto_unref(rule->ofproto);
     rule->ofproto->ofproto_class->rule_dealloc(rule);
 }
 
@@ -3069,6 +3116,9 @@ group_destroy_cb(struct ofgroup *group)
                                                 &group->props));
     ofputil_bucket_list_destroy(CONST_CAST(struct ovs_list *,
                                            &group->buckets));
+    /* ofproto_unref() must be called first. It is possible because ofproto
+     * destruction is deferred by an RCU grace period. */
+    ofproto_unref(group->ofproto);
     group->ofproto->ofproto_class->group_dealloc(group);
 }
 
@@ -5271,10 +5321,15 @@ ofproto_rule_create(struct ofproto *ofproto, struct cls_rule *cr,
     struct rule *rule;
     enum ofperr error;
 
+    if (!ofproto_try_ref(ofproto)) {
+        return OFPERR_OFPFMFC_UNKNOWN;
+    }
+
     /* Allocate new rule. */
     rule = ofproto->ofproto_class->rule_alloc();
     if (!rule) {
         cls_rule_destroy(cr);
+        ofproto_unref(ofproto);
         VLOG_WARN_RL(&rl, "%s: failed to allocate a rule.", ofproto->name);
         return OFPERR_OFPFMFC_UNKNOWN;
     }
@@ -6797,9 +6852,9 @@ static void
 meter_delete_all(struct ofproto *ofproto)
     OVS_REQUIRES(ofproto_mutex)
 {
-    struct meter *meter, *next;
+    struct meter *meter;
 
-    HMAP_FOR_EACH_SAFE (meter, next, node, &ofproto->meters) {
+    HMAP_FOR_EACH_SAFE (meter, node, &ofproto->meters) {
         hmap_remove(&ofproto->meters, &meter->node);
         meter_destroy(ofproto, meter);
     }
@@ -7339,8 +7394,13 @@ init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm,
         return OFPERR_OFPGMFC_BAD_TYPE;
     }
 
+    if (!ofproto_try_ref(ofproto)) {
+        return OFPERR_OFPFMFC_UNKNOWN;
+    }
+
     *ofgroup = ofproto->ofproto_class->group_alloc();
     if (!*ofgroup) {
+        ofproto_unref(ofproto);
         VLOG_WARN_RL(&rl, "%s: failed to allocate group", ofproto->name);
         return OFPERR_OFPGMFC_OUT_OF_GROUPS;
     }
@@ -7377,6 +7437,7 @@ init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm,
                                                     &(*ofgroup)->props));
         ofputil_bucket_list_destroy(CONST_CAST(struct ovs_list *,
                                                &(*ofgroup)->buckets));
+        ofproto_unref(ofproto);
         ofproto->ofproto_class->group_dealloc(*ofgroup);
     }
     return error;
@@ -9138,8 +9199,8 @@ oftable_configure_eviction(struct oftable *table, unsigned int eviction,
 
     /* Destroy existing eviction groups, then destroy and recreate data
      * structures to recover memory. */
-    struct eviction_group *evg, *next;
-    HMAP_FOR_EACH_SAFE (evg, next, id_node, &table->eviction_groups_by_id) {
+    struct eviction_group *evg;
+    HMAP_FOR_EACH_SAFE (evg, id_node, &table->eviction_groups_by_id) {
         eviction_group_destroy(table, evg);
     }
     hmap_destroy(&table->eviction_groups_by_id);
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index b0262da2df..4e15167ab7 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -563,6 +563,10 @@ int ofproto_port_get_cfm_status(const struct ofproto *,
 enum ofputil_table_miss ofproto_table_get_miss_config(const struct ofproto *,
                                                       uint8_t table_id);
 
+void ofproto_ref(struct ofproto *);
+void ofproto_unref(struct ofproto *);
+bool ofproto_try_ref(struct ofproto *);
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/ovsdb/condition.c b/ovsdb/condition.c
index 388dd54a16..9aa3788dbb 100644
--- a/ovsdb/condition.c
+++ b/ovsdb/condition.c
@@ -220,13 +220,13 @@ ovsdb_condition_optimize(struct ovsdb_condition *cnd)
 static void
 ovsdb_condition_optimize_destroy(struct ovsdb_condition *cnd)
 {
-     struct shash_node *node, *next;
+     struct shash_node *node;
 
-     SHASH_FOR_EACH_SAFE (node, next, &cnd->o_columns) {
+     SHASH_FOR_EACH_SAFE (node, &cnd->o_columns) {
          struct ovsdb_o_column *o_column = node->data;
-         struct ovsdb_o_clause *c, *c_next;
+         struct ovsdb_o_clause *c;
 
-         HMAP_FOR_EACH_SAFE(c, c_next, hmap_node, &o_column->o_clauses) {
+         HMAP_FOR_EACH_SAFE (c, hmap_node, &o_column->o_clauses) {
              hmap_remove(&o_column->o_clauses, &c->hmap_node);
              free(c);
          }
diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c
index 351c39d8aa..916a1f414e 100644
--- a/ovsdb/jsonrpc-server.c
+++ b/ovsdb/jsonrpc-server.c
@@ -197,9 +197,9 @@ ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *svr,
 void
 ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr)
 {
-    struct shash_node *node, *next;
+    struct shash_node *node;
 
-    SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) {
+    SHASH_FOR_EACH_SAFE (node, &svr->remotes) {
         ovsdb_jsonrpc_server_del_remote(node);
     }
     shash_destroy(&svr->remotes);
@@ -227,9 +227,9 @@ void
 ovsdb_jsonrpc_server_set_remotes(struct ovsdb_jsonrpc_server *svr,
                                  const struct shash *new_remotes)
 {
-    struct shash_node *node, *next;
+    struct shash_node *node;
 
-    SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) {
+    SHASH_FOR_EACH_SAFE (node, &svr->remotes) {
         struct ovsdb_jsonrpc_remote *remote = node->data;
         struct ovsdb_jsonrpc_options *options
             = shash_find_data(new_remotes, node->name);
@@ -585,9 +585,9 @@ ovsdb_jsonrpc_session_set_options(struct ovsdb_jsonrpc_session *session,
 static void
 ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *remote)
 {
-    struct ovsdb_jsonrpc_session *s, *next;
+    struct ovsdb_jsonrpc_session *s;
 
-    LIST_FOR_EACH_SAFE (s, next, node, &remote->sessions) {
+    LIST_FOR_EACH_SAFE (s, node, &remote->sessions) {
         int error = ovsdb_jsonrpc_session_run(s);
         if (error) {
             ovsdb_jsonrpc_session_close(s);
@@ -642,9 +642,9 @@ ovsdb_jsonrpc_session_get_memory_usage_all(
 static void
 ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *remote)
 {
-    struct ovsdb_jsonrpc_session *s, *next;
+    struct ovsdb_jsonrpc_session *s;
 
-    LIST_FOR_EACH_SAFE (s, next, node, &remote->sessions) {
+    LIST_FOR_EACH_SAFE (s, node, &remote->sessions) {
         ovsdb_jsonrpc_session_close(s);
     }
 }
@@ -660,9 +660,9 @@ static void
 ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *remote,
                                     bool force, const char *comment)
 {
-    struct ovsdb_jsonrpc_session *s, *next;
+    struct ovsdb_jsonrpc_session *s;
 
-    LIST_FOR_EACH_SAFE (s, next, node, &remote->sessions) {
+    LIST_FOR_EACH_SAFE (s, node, &remote->sessions) {
         if (force || !s->db_change_aware) {
             jsonrpc_session_force_reconnect(s->js);
             if (comment && jsonrpc_session_is_connected(s->js)) {
@@ -909,9 +909,9 @@ error:
 static void
 ovsdb_jsonrpc_session_unlock_all(struct ovsdb_jsonrpc_session *s)
 {
-    struct ovsdb_lock_waiter *waiter, *next;
+    struct ovsdb_lock_waiter *waiter;
 
-    HMAP_FOR_EACH_SAFE (waiter, next, session_node, &s->up.waiters) {
+    HMAP_FOR_EACH_SAFE (waiter, session_node, &s->up.waiters) {
         ovsdb_jsonrpc_session_unlock__(waiter);
     }
 }
@@ -1198,8 +1198,8 @@ static void
 ovsdb_jsonrpc_trigger_remove__(struct ovsdb_jsonrpc_session *s,
                                struct ovsdb *db)
 {
-    struct ovsdb_jsonrpc_trigger *t, *next;
-    HMAP_FOR_EACH_SAFE (t, next, hmap_node, &s->triggers) {
+    struct ovsdb_jsonrpc_trigger *t;
+    HMAP_FOR_EACH_SAFE (t, hmap_node, &s->triggers) {
         if (!db || t->trigger.db == db) {
             ovsdb_jsonrpc_trigger_complete(t);
         }
@@ -1226,8 +1226,8 @@ ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *s)
 static void
 ovsdb_jsonrpc_trigger_complete_done(struct ovsdb_jsonrpc_session *s)
 {
-    struct ovsdb_jsonrpc_trigger *trigger, *next;
-    LIST_FOR_EACH_SAFE (trigger, next, trigger.node, &s->up.completions) {
+    struct ovsdb_jsonrpc_trigger *trigger;
+    LIST_FOR_EACH_SAFE (trigger, trigger.node, &s->up.completions) {
         ovsdb_jsonrpc_trigger_complete(trigger);
     }
 }
@@ -1688,8 +1688,8 @@ ovsdb_jsonrpc_monitor_preremove_db(struct ovsdb_jsonrpc_session *s,
 {
     ovs_assert(db);
 
-    struct ovsdb_jsonrpc_monitor *m, *next;
-    HMAP_FOR_EACH_SAFE (m, next, node, &s->monitors) {
+    struct ovsdb_jsonrpc_monitor *m;
+    HMAP_FOR_EACH_SAFE (m, node, &s->monitors) {
         if (m->db == db) {
             ovsdb_jsonrpc_monitor_destroy(m, true);
         }
@@ -1700,9 +1700,9 @@ ovsdb_jsonrpc_monitor_preremove_db(struct ovsdb_jsonrpc_session *s,
 static void
 ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s)
 {
-    struct ovsdb_jsonrpc_monitor *m, *next;
+    struct ovsdb_jsonrpc_monitor *m;
 
-    HMAP_FOR_EACH_SAFE (m, next, node, &s->monitors) {
+    HMAP_FOR_EACH_SAFE (m, node, &s->monitors) {
         ovsdb_jsonrpc_monitor_destroy(m, false);
     }
 }
diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c
index 0f222cc992..952fa902e4 100644
--- a/ovsdb/monitor.c
+++ b/ovsdb/monitor.c
@@ -638,14 +638,14 @@ ovsdb_monitor_change_set_destroy(struct ovsdb_monitor_change_set *mcs)
 {
     ovs_list_remove(&mcs->list_node);
 
-    struct ovsdb_monitor_change_set_for_table *mcst, *next_mcst;
-    LIST_FOR_EACH_SAFE (mcst, next_mcst, list_in_change_set,
+    struct ovsdb_monitor_change_set_for_table *mcst;
+    LIST_FOR_EACH_SAFE (mcst, list_in_change_set,
                         &mcs->change_set_for_tables) {
         ovs_list_remove(&mcst->list_in_change_set);
         ovs_list_remove(&mcst->list_in_mt);
 
-        struct ovsdb_monitor_row *row, *next;
-        HMAP_FOR_EACH_SAFE (row, next, hmap_node, &mcst->rows) {
+        struct ovsdb_monitor_row *row;
+        HMAP_FOR_EACH_SAFE (row, hmap_node, &mcst->rows) {
             hmap_remove(&mcst->rows, &row->hmap_node);
             ovsdb_monitor_row_destroy(mcst->mt, row, mcst->n_columns);
         }
@@ -700,13 +700,13 @@ void
 ovsdb_monitor_session_condition_destroy(
                            struct ovsdb_monitor_session_condition *condition)
 {
-    struct shash_node *node, *next;
+    struct shash_node *node;
 
     if (!condition) {
         return;
     }
 
-    SHASH_FOR_EACH_SAFE (node, next, &condition->tables) {
+    SHASH_FOR_EACH_SAFE (node, &condition->tables) {
         struct ovsdb_monitor_table_condition *mtc = node->data;
 
         ovsdb_condition_destroy(&mtc->new_condition);
@@ -1122,11 +1122,11 @@ ovsdb_monitor_compose_update(
     json = NULL;
     struct ovsdb_monitor_change_set_for_table *mcst;
     LIST_FOR_EACH (mcst, list_in_change_set, &mcs->change_set_for_tables) {
-        struct ovsdb_monitor_row *row, *next;
+        struct ovsdb_monitor_row *row;
         struct json *table_json = NULL;
         struct ovsdb_monitor_table *mt = mcst->mt;
 
-        HMAP_FOR_EACH_SAFE (row, next, hmap_node, &mcst->rows) {
+        HMAP_FOR_EACH_SAFE (row, hmap_node, &mcst->rows) {
             struct json *row_json;
             row_json = (*row_update)(mt, condition, OVSDB_MONITOR_ROW, row,
                                      initial, changed, mcst->n_columns);
@@ -1711,8 +1711,8 @@ ovsdb_monitor_destroy(struct ovsdb_monitor *dbmon)
     ovsdb_monitor_json_cache_flush(dbmon);
     hmap_destroy(&dbmon->json_cache);
 
-    struct ovsdb_monitor_change_set *cs, *cs_next;
-    LIST_FOR_EACH_SAFE (cs, cs_next, list_node, &dbmon->change_sets) {
+    struct ovsdb_monitor_change_set *cs;
+    LIST_FOR_EACH_SAFE (cs, list_node, &dbmon->change_sets) {
         ovsdb_monitor_change_set_destroy(cs);
     }
 
@@ -1760,14 +1760,14 @@ ovsdb_monitors_commit(struct ovsdb *db, const struct ovsdb_txn *txn)
 void
 ovsdb_monitors_remove(struct ovsdb *db)
 {
-    struct ovsdb_monitor *m, *next_m;
+    struct ovsdb_monitor *m;
 
-    LIST_FOR_EACH_SAFE (m, next_m, list_node, &db->monitors) {
-        struct jsonrpc_monitor_node *jm, *next_jm;
+    LIST_FOR_EACH_SAFE (m, list_node, &db->monitors) {
+        struct jsonrpc_monitor_node *jm;
 
         /* Delete all front-end monitors.  Removing the last front-end monitor
          * will also destroy the corresponding ovsdb_monitor. */
-        LIST_FOR_EACH_SAFE (jm, next_jm, node, &m->jsonrpc_monitors) {
+        LIST_FOR_EACH_SAFE (jm, node, &m->jsonrpc_monitors) {
             ovsdb_jsonrpc_monitor_destroy(jm->jsonrpc_monitor, false);
         }
     }
@@ -1789,14 +1789,14 @@ ovsdb_monitor_get_memory_usage(struct simap *usage)
 void
 ovsdb_monitor_prereplace_db(struct ovsdb *db)
 {
-    struct ovsdb_monitor *m, *next_m;
+    struct ovsdb_monitor *m;
 
-    LIST_FOR_EACH_SAFE (m, next_m, list_node, &db->monitors) {
-        struct jsonrpc_monitor_node *jm, *next_jm;
+    LIST_FOR_EACH_SAFE (m, list_node, &db->monitors) {
+        struct jsonrpc_monitor_node *jm;
 
         /* Delete all front-end monitors.  Removing the last front-end monitor
          * will also destroy the corresponding ovsdb_monitor. */
-        LIST_FOR_EACH_SAFE (jm, next_jm, node, &m->jsonrpc_monitors) {
+        LIST_FOR_EACH_SAFE (jm, node, &m->jsonrpc_monitors) {
             ovsdb_jsonrpc_monitor_destroy(jm->jsonrpc_monitor, true);
         }
     }
diff --git a/ovsdb/ovsdb-idlc.in b/ovsdb/ovsdb-idlc.in
index 10a70ae26f..13c5359395 100755
--- a/ovsdb/ovsdb-idlc.in
+++ b/ovsdb/ovsdb-idlc.in
@@ -251,10 +251,18 @@ const struct %(s)s *%(s)s_table_first(const struct %(s)s_table *);
         for ((ROW) = %(s)s_table_first(TABLE); \\
              (ROW); \\
              (ROW) = %(s)s_next(ROW))
-#define %(S)s_TABLE_FOR_EACH_SAFE(ROW, NEXT, TABLE) \\
+#define %(S)s_TABLE_FOR_EACH_SAFE_LONG(ROW, NEXT, TABLE) \\
         for ((ROW) = %(s)s_table_first(TABLE); \\
              (ROW) ? ((NEXT) = %(s)s_next(ROW), 1) : 0; \\
              (ROW) = (NEXT))
+#define %(S)s_TABLE_FOR_EACH_SAFE_SHORT(ROW, TABLE) \\
+        for (const struct %(s)s * ROW__next = ((ROW) = %(s)s_table_first(TABLE), NULL); \\
+             (ROW) ? (ROW__next = %(s)s_next(ROW), 1) : (ROW__next = NULL, 0); \\
+             (ROW) = ROW__next)
+#define %(S)s_TABLE_FOR_EACH_SAFE(...)                                        \\
+        OVERLOAD_SAFE_MACRO(%(S)s_TABLE_FOR_EACH_SAFE_LONG,                   \\
+                            %(S)s_TABLE_FOR_EACH_SAFE_SHORT, 3, __VA_ARGS__)
+
 
 const struct %(s)s *%(s)s_get_for_uuid(const struct ovsdb_idl *, const struct uuid *);
 const struct %(s)s *%(s)s_table_get_for_uuid(const struct %(s)s_table *, const struct uuid *);
@@ -264,10 +272,17 @@ const struct %(s)s *%(s)s_next(const struct %(s)s *);
         for ((ROW) = %(s)s_first(IDL); \\
              (ROW); \\
              (ROW) = %(s)s_next(ROW))
-#define %(S)s_FOR_EACH_SAFE(ROW, NEXT, IDL) \\
+#define %(S)s_FOR_EACH_SAFE_LONG(ROW, NEXT, IDL) \\
         for ((ROW) = %(s)s_first(IDL); \\
              (ROW) ? ((NEXT) = %(s)s_next(ROW), 1) : 0; \\
              (ROW) = (NEXT))
+#define %(S)s_FOR_EACH_SAFE_SHORT(ROW, IDL) \\
+        for (const struct %(s)s * ROW__next = ((ROW) = %(s)s_first(IDL), NULL); \\
+             (ROW) ? (ROW__next = %(s)s_next(ROW), 1) : (ROW__next = NULL, 0); \\
+             (ROW) = ROW__next)
+#define %(S)s_FOR_EACH_SAFE(...)                                         \\
+        OVERLOAD_SAFE_MACRO(%(S)s_FOR_EACH_SAFE_LONG,                    \\
+                            %(S)s_FOR_EACH_SAFE_SHORT, 3, __VA_ARGS__)
 
 unsigned int %(s)s_get_seqno(const struct ovsdb_idl *);
 unsigned int %(s)s_row_get_seqno(const struct %(s)s *row, enum ovsdb_idl_change change);
diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
index 9fe90592ea..774416fc7a 100644
--- a/ovsdb/ovsdb-server.c
+++ b/ovsdb/ovsdb-server.c
@@ -26,6 +26,7 @@
 #include "command-line.h"
 #include "daemon.h"
 #include "dirs.h"
+#include "dns-resolve.h"
 #include "openvswitch/dynamic-string.h"
 #include "fatal-signal.h"
 #include "file.h"
@@ -228,8 +229,7 @@ main_loop(struct server_config *config,
 
         ovsdb_relay_run();
 
-        struct shash_node *next;
-        SHASH_FOR_EACH_SAFE (node, next, all_dbs) {
+        SHASH_FOR_EACH_SAFE (node, all_dbs) {
             struct db *db = node->data;
             ovsdb_txn_history_run(db->db);
             ovsdb_storage_run(db->db->storage);
@@ -321,7 +321,7 @@ main(int argc, char *argv[])
     FILE *config_tmpfile;
     struct server_config server_config;
     struct shash all_dbs;
-    struct shash_node *node, *next;
+    struct shash_node *node;
     int replication_probe_interval = REPLICATION_DEFAULT_PROBE_INTERVAL;
 
     ovs_cmdl_proctitle_init(argc, argv);
@@ -329,6 +329,7 @@ main(int argc, char *argv[])
     service_start(&argc, &argv);
     fatal_ignore_sigpipe();
     process_init();
+    dns_resolve_init(true);
 
     bool active = false;
     parse_options(argc, argv, &db_filenames, &remotes, &unixctl_path,
@@ -490,7 +491,7 @@ main(int argc, char *argv[])
     main_loop(&server_config, jsonrpc, &all_dbs, unixctl, &remotes,
               run_process, &exiting, &is_backup);
 
-    SHASH_FOR_EACH_SAFE(node, next, &all_dbs) {
+    SHASH_FOR_EACH_SAFE (node, &all_dbs) {
         struct db *db = node->data;
         close_db(&server_config, db, NULL);
         shash_delete(&all_dbs, node);
@@ -511,6 +512,7 @@ main(int argc, char *argv[])
                       run_command, process_status_msg(status));
         }
     }
+    dns_resolve_destroy();
     perf_counters_destroy();
     service_stop();
     return 0;
@@ -1240,8 +1242,8 @@ update_server_status(struct shash *all_dbs)
 
     /* Update rows for databases that still exist.
      * Delete rows for databases that no longer exist. */
-    const struct ovsdb_row *row, *next_row;
-    HMAP_FOR_EACH_SAFE (row, next_row, hmap_node, &database_table->rows) {
+    const struct ovsdb_row *row;
+    HMAP_FOR_EACH_SAFE (row, hmap_node, &database_table->rows) {
         const char *name;
         ovsdb_util_read_string_column(row, "name", &name);
         struct db *db = shash_find_data(all_dbs, name);
diff --git a/ovsdb/ovsdb-tool.c b/ovsdb/ovsdb-tool.c
index d4a9e34cc4..df2e373c3c 100644
--- a/ovsdb/ovsdb-tool.c
+++ b/ovsdb/ovsdb-tool.c
@@ -1579,15 +1579,14 @@ do_check_cluster(struct ovs_cmdl_context *ctx)
     }
     free(c.servers);
 
-    struct commit *next_commit;
-    HMAP_FOR_EACH_SAFE (commit, next_commit, hmap_node, &c.commits) {
+    HMAP_FOR_EACH_SAFE (commit, hmap_node, &c.commits) {
         hmap_remove(&c.commits, &commit->hmap_node);
         free(commit);
     }
     hmap_destroy(&c.commits);
 
-    struct leader *leader, *next_leader;
-    HMAP_FOR_EACH_SAFE (leader, next_leader, hmap_node, &c.leaders) {
+    struct leader *leader;
+    HMAP_FOR_EACH_SAFE (leader, hmap_node, &c.leaders) {
         hmap_remove(&c.leaders, &leader->hmap_node);
         free(leader);
     }
diff --git a/ovsdb/ovsdb.c b/ovsdb/ovsdb.c
index e6d866182c..91b4a01af8 100644
--- a/ovsdb/ovsdb.c
+++ b/ovsdb/ovsdb.c
@@ -571,8 +571,8 @@ ovsdb_replace(struct ovsdb *dst, struct ovsdb *src)
     ovsdb_monitor_prereplace_db(dst);
 
     /* Cancel triggers. */
-    struct ovsdb_trigger *trigger, *next;
-    LIST_FOR_EACH_SAFE (trigger, next, node, &dst->triggers) {
+    struct ovsdb_trigger *trigger;
+    LIST_FOR_EACH_SAFE (trigger, node, &dst->triggers) {
         ovsdb_trigger_prereplace_db(trigger);
     }
 
diff --git a/ovsdb/query.c b/ovsdb/query.c
index de74519989..eebe564127 100644
--- a/ovsdb/query.c
+++ b/ovsdb/query.c
@@ -40,9 +40,9 @@ ovsdb_query(struct ovsdb_table *table, const struct ovsdb_condition *cnd,
         }
     } else {
         /* Linear scan. */
-        const struct ovsdb_row *row, *next;
+        const struct ovsdb_row *row;
 
-        HMAP_FOR_EACH_SAFE (row, next, hmap_node, &table->rows) {
+        HMAP_FOR_EACH_SAFE (row, hmap_node, &table->rows) {
             if (ovsdb_condition_match_every_clause(row, cnd) &&
                 !output_row(row, aux)) {
                 break;
diff --git a/ovsdb/raft-private.c b/ovsdb/raft-private.c
index 30760233ee..e685c8103b 100644
--- a/ovsdb/raft-private.c
+++ b/ovsdb/raft-private.c
@@ -36,7 +36,10 @@ raft_address_validate(const char *address)
         return NULL;
     } else if (!strncmp(address, "ssl:", 4) || !strncmp(address, "tcp:", 4)) {
         struct sockaddr_storage ss;
-        if (!inet_parse_active(address + 4, -1, &ss, true)) {
+        bool dns_failure = false;
+
+        if (!inet_parse_active(address + 4, -1, &ss, true, &dns_failure)
+            && !dns_failure) {
             return ovsdb_error(NULL, "%s: syntax error in address", address);
         }
         return NULL;
@@ -147,8 +150,8 @@ raft_server_destroy(struct raft_server *s)
 void
 raft_servers_destroy(struct hmap *servers)
 {
-    struct raft_server *s, *next;
-    HMAP_FOR_EACH_SAFE (s, next, hmap_node, servers) {
+    struct raft_server *s;
+    HMAP_FOR_EACH_SAFE (s, hmap_node, servers) {
         hmap_remove(servers, &s->hmap_node);
         raft_server_destroy(s);
     }
diff --git a/ovsdb/raft.c b/ovsdb/raft.c
index 1a3447a8dd..530c5e5a3d 100644
--- a/ovsdb/raft.c
+++ b/ovsdb/raft.c
@@ -74,6 +74,7 @@ enum raft_failure_test {
     FT_CRASH_BEFORE_SEND_EXEC_REQ,
     FT_CRASH_AFTER_SEND_EXEC_REQ,
     FT_CRASH_AFTER_RECV_APPEND_REQ_UPDATE,
+    FT_CRASH_BEFORE_SEND_SNAPSHOT_REP,
     FT_DELAY_ELECTION,
     FT_DONT_SEND_VOTE_REQUEST,
     FT_STOP_RAFT_RPC,
@@ -379,12 +380,19 @@ static bool raft_handle_write_error(struct raft *, struct ovsdb_error *);
 static void raft_run_reconfigure(struct raft *);
 
 static void raft_set_leader(struct raft *, const struct uuid *sid);
+
 static struct raft_server *
 raft_find_server(const struct raft *raft, const struct uuid *sid)
 {
     return raft_server_find(&raft->servers, sid);
 }
 
+static struct raft_server *
+raft_find_new_server(struct raft *raft, const struct uuid *uuid)
+{
+    return raft_server_find(&raft->add_servers, uuid);
+}
+
 static char *
 raft_make_address_passive(const char *address_)
 {
@@ -692,8 +700,8 @@ static void
 raft_set_servers(struct raft *raft, const struct hmap *new_servers,
                  enum vlog_level level)
 {
-    struct raft_server *s, *next;
-    HMAP_FOR_EACH_SAFE (s, next, hmap_node, &raft->servers) {
+    struct raft_server *s;
+    HMAP_FOR_EACH_SAFE (s, hmap_node, &raft->servers) {
         if (!raft_server_find(new_servers, &s->sid)) {
             ovs_assert(s != raft->remove_server);
 
@@ -703,7 +711,7 @@ raft_set_servers(struct raft *raft, const struct hmap *new_servers,
         }
     }
 
-    HMAP_FOR_EACH_SAFE (s, next, hmap_node, new_servers) {
+    HMAP_FOR_EACH_SAFE (s, hmap_node, new_servers) {
         if (!raft_find_server(raft, &s->sid)) {
             VLOG(level, "server %s added to configuration", s->nickname);
 
@@ -1376,8 +1384,8 @@ raft_close__(struct raft *raft)
         raft->remove_server = NULL;
     }
 
-    struct raft_conn *conn, *next;
-    LIST_FOR_EACH_SAFE (conn, next, list_node, &raft->conns) {
+    struct raft_conn *conn;
+    LIST_FOR_EACH_SAFE (conn, list_node, &raft->conns) {
         raft_conn_close(conn);
     }
 }
@@ -1713,8 +1721,8 @@ raft_waiters_run(struct raft *raft)
     }
 
     uint64_t cur = ovsdb_log_commit_progress(raft->log);
-    struct raft_waiter *w, *next;
-    LIST_FOR_EACH_SAFE (w, next, list_node, &raft->waiters) {
+    struct raft_waiter *w;
+    LIST_FOR_EACH_SAFE (w, list_node, &raft->waiters) {
         if (cur < w->commit_ticket) {
             break;
         }
@@ -1736,8 +1744,8 @@ raft_waiters_wait(struct raft *raft)
 static void
 raft_waiters_destroy(struct raft *raft)
 {
-    struct raft_waiter *w, *next;
-    LIST_FOR_EACH_SAFE (w, next, list_node, &raft->waiters) {
+    struct raft_waiter *w;
+    LIST_FOR_EACH_SAFE (w, list_node, &raft->waiters) {
         raft_waiter_destroy(w);
     }
 }
@@ -1867,6 +1875,8 @@ raft_open_conn(struct raft *raft, const char *address, const struct uuid *sid)
 static void
 raft_conn_close(struct raft_conn *conn)
 {
+    VLOG_DBG("closing connection to server %s (%s)",
+             conn->nickname, jsonrpc_session_get_name(conn->js));
     jsonrpc_session_close(conn->js);
     ovs_list_remove(&conn->list_node);
     free(conn->nickname);
@@ -1957,16 +1967,29 @@ raft_run(struct raft *raft)
     }
 
     /* Close unneeded sessions. */
-    struct raft_conn *next;
-    LIST_FOR_EACH_SAFE (conn, next, list_node, &raft->conns) {
+    struct raft_server *server;
+    LIST_FOR_EACH_SAFE (conn, list_node, &raft->conns) {
         if (!raft_conn_should_stay_open(raft, conn)) {
+            server = raft_find_new_server(raft, &conn->sid);
+            if (server) {
+                /* We only have one incoming connection from joining servers,
+                 * so if it's closed, we need to destroy the record about the
+                 * server.  This way the process can be started over on the
+                 * next join request. */
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
+                VLOG_INFO_RL(&rl, "cluster "CID_FMT": server %s (%s) "
+                                  "disconnected while joining",
+                                  CID_ARGS(&raft->cid),
+                                  server->nickname, server->address);
+                hmap_remove(&raft->add_servers, &server->hmap_node);
+                raft_server_destroy(server);
+            }
             raft->n_disconnections++;
             raft_conn_close(conn);
         }
     }
 
     /* Open needed sessions. */
-    struct raft_server *server;
     HMAP_FOR_EACH (server, hmap_node, &raft->servers) {
         raft_open_conn(raft, server->address, &server->sid);
     }
@@ -2039,8 +2062,8 @@ raft_run(struct raft *raft)
          * commands becomes new leader: the pending commands can still complete
          * if the crashed leader has replicated the transactions to majority of
          * followers before it crashed. */
-        struct raft_command *cmd, *next_cmd;
-        HMAP_FOR_EACH_SAFE (cmd, next_cmd, hmap_node, &raft->commands) {
+        struct raft_command *cmd;
+        HMAP_FOR_EACH_SAFE (cmd, hmap_node, &raft->commands) {
             if (cmd->timestamp
                 && now - cmd->timestamp > raft->election_timer * 2) {
                 raft_command_complete(raft, cmd, RAFT_CMD_TIMEOUT);
@@ -2243,8 +2266,8 @@ raft_command_initiate(struct raft *raft,
 static void
 log_all_commands(struct raft *raft)
 {
-    struct raft_command *cmd, *next;
-    HMAP_FOR_EACH_SAFE (cmd, next, hmap_node, &raft->commands) {
+    struct raft_command *cmd;
+    HMAP_FOR_EACH_SAFE (cmd, hmap_node, &raft->commands) {
         VLOG_DBG("raft command eid: "UUID_FMT, UUID_ARGS(&cmd->eid));
     }
 }
@@ -2398,8 +2421,8 @@ raft_command_complete(struct raft *raft,
 static void
 raft_complete_all_commands(struct raft *raft, enum raft_command_status status)
 {
-    struct raft_command *cmd, *next;
-    HMAP_FOR_EACH_SAFE (cmd, next, hmap_node, &raft->commands) {
+    struct raft_command *cmd;
+    HMAP_FOR_EACH_SAFE (cmd, hmap_node, &raft->commands) {
         raft_command_complete(raft, cmd, status);
     }
 }
@@ -3354,12 +3377,6 @@ raft_find_peer(struct raft *raft, const struct uuid *uuid)
     return s && !uuid_equals(&raft->sid, &s->sid) ? s : NULL;
 }
 
-static struct raft_server *
-raft_find_new_server(struct raft *raft, const struct uuid *uuid)
-{
-    return raft_server_find(&raft->add_servers, uuid);
-}
-
 /* Figure 3.1: "If there exists an N such that N > commitIndex, a
  * majority of matchIndex[i] >= N, and log[N].term == currentTerm, set
  * commitIndex = N (sections 3.5 and 3.6)." */
@@ -4142,6 +4159,10 @@ static void
 raft_handle_install_snapshot_request(
     struct raft *raft, const struct raft_install_snapshot_request *rq)
 {
+    if (failure_test == FT_CRASH_BEFORE_SEND_SNAPSHOT_REP) {
+        ovs_fatal(0, "Raft test: crash before sending install_snapshot_reply");
+    }
+
     if (raft_handle_install_snapshot_request__(raft, rq)) {
         union raft_rpc rpy = {
             .install_snapshot_reply = {
@@ -4940,6 +4961,8 @@ raft_unixctl_failure_test(struct unixctl_conn *conn OVS_UNUSED,
         failure_test = FT_CRASH_AFTER_SEND_EXEC_REQ;
     } else if (!strcmp(test, "crash-after-receiving-append-request-update")) {
         failure_test = FT_CRASH_AFTER_RECV_APPEND_REQ_UPDATE;
+    } else if (!strcmp(test, "crash-before-sending-install-snapshot-reply")) {
+        failure_test = FT_CRASH_BEFORE_SEND_SNAPSHOT_REP;
     } else if (!strcmp(test, "delay-election")) {
         failure_test = FT_DELAY_ELECTION;
         struct raft *raft;
diff --git a/ovsdb/relay.c b/ovsdb/relay.c
index ef0e44d340..122ee8c52f 100644
--- a/ovsdb/relay.c
+++ b/ovsdb/relay.c
@@ -269,9 +269,9 @@ ovsdb_relay_clear(struct ovsdb *db)
 
     SHASH_FOR_EACH (table_node, &db->tables) {
         struct ovsdb_table *table = table_node->data;
-        struct ovsdb_row *row, *next;
+        struct ovsdb_row *row;
 
-        HMAP_FOR_EACH_SAFE (row, next, hmap_node, &table->rows) {
+        HMAP_FOR_EACH_SAFE (row, hmap_node, &table->rows) {
             ovsdb_txn_row_delete(txn, row);
         }
     }
diff --git a/ovsdb/replication.c b/ovsdb/replication.c
index d8b56d8131..477c69d701 100644
--- a/ovsdb/replication.c
+++ b/ovsdb/replication.c
@@ -549,8 +549,8 @@ reset_database(struct ovsdb *db)
         /* Delete all rows if the table is not excluded. */
         if (!excluded_tables_find(db->schema->name, table_node->name)) {
             struct ovsdb_table *table = table_node->data;
-            struct ovsdb_row *row, *next;
-            HMAP_FOR_EACH_SAFE (row, next, hmap_node, &table->rows) {
+            struct ovsdb_row *row;
+            HMAP_FOR_EACH_SAFE (row, hmap_node, &table->rows) {
                 ovsdb_txn_row_delete(txn, row);
             }
         }
@@ -769,9 +769,9 @@ replication_dbs_destroy(void)
         return;
     }
 
-    struct shash_node *node, *next;
+    struct shash_node *node;
 
-    SHASH_FOR_EACH_SAFE (node, next, replication_dbs) {
+    SHASH_FOR_EACH_SAFE (node, replication_dbs) {
         hmap_remove(&replication_dbs->map, &node->node);
         struct replication_db *rdb = node->data;
         if (rdb->active_db_schema) {
diff --git a/ovsdb/table.c b/ovsdb/table.c
index 455a3663fe..2184701ec1 100644
--- a/ovsdb/table.c
+++ b/ovsdb/table.c
@@ -309,10 +309,10 @@ void
 ovsdb_table_destroy(struct ovsdb_table *table)
 {
     if (table) {
-        struct ovsdb_row *row, *next;
+        struct ovsdb_row *row;
         size_t i;
 
-        HMAP_FOR_EACH_SAFE (row, next, hmap_node, &table->rows) {
+        HMAP_FOR_EACH_SAFE (row, hmap_node, &table->rows) {
             ovsdb_row_destroy(row);
         }
         hmap_destroy(&table->rows);
diff --git a/ovsdb/transaction-forward.c b/ovsdb/transaction-forward.c
index d15f2f1d6d..963e937957 100644
--- a/ovsdb/transaction-forward.c
+++ b/ovsdb/transaction-forward.c
@@ -126,10 +126,10 @@ ovsdb_txn_forward_steal_reply(struct ovsdb_txn_forward *txn_fwd)
 void
 ovsdb_txn_forward_run(struct ovsdb *db, struct ovsdb_cs *cs)
 {
-    struct ovsdb_txn_forward *t, *next;
+    struct ovsdb_txn_forward *t;
 
     /* Send all transactions that needs to be forwarded. */
-    LIST_FOR_EACH_SAFE (t, next, new_node, &db->txn_forward_new) {
+    LIST_FOR_EACH_SAFE (t, new_node, &db->txn_forward_new) {
         if (!ovsdb_cs_may_send_transaction(cs)) {
             break;
         }
@@ -167,9 +167,9 @@ ovsdb_txn_forward_cancel(struct ovsdb *db, struct ovsdb_txn_forward *txn_fwd)
 void
 ovsdb_txn_forward_cancel_all(struct ovsdb *db, bool sent_only)
 {
-    struct ovsdb_txn_forward *t, *next;
+    struct ovsdb_txn_forward *t;
 
-    HMAP_FOR_EACH_SAFE (t, next, sent_node, &db->txn_forward_sent) {
+    HMAP_FOR_EACH_SAFE (t, sent_node, &db->txn_forward_sent) {
         ovsdb_txn_forward_cancel(db, t);
     }
 
@@ -177,7 +177,7 @@ ovsdb_txn_forward_cancel_all(struct ovsdb *db, bool sent_only)
         return;
     }
 
-    LIST_FOR_EACH_SAFE (t, next, new_node, &db->txn_forward_new) {
+    LIST_FOR_EACH_SAFE (t, new_node, &db->txn_forward_new) {
         ovsdb_txn_forward_cancel(db, t);
     }
 }
diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
index db86d847c3..3a6ddfa1df 100644
--- a/ovsdb/transaction.c
+++ b/ovsdb/transaction.c
@@ -159,15 +159,15 @@ ovsdb_txn_row_abort(struct ovsdb_txn *txn OVS_UNUSED,
         hmap_replace(&new->table->rows, &new->hmap_node, &old->hmap_node);
     }
 
-    struct ovsdb_weak_ref *weak, *next;
-    LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->deleted_refs) {
+    struct ovsdb_weak_ref *weak;
+    LIST_FOR_EACH_SAFE (weak, src_node, &txn_row->deleted_refs) {
         ovs_list_remove(&weak->src_node);
         ovs_list_init(&weak->src_node);
         if (hmap_node_is_null(&weak->dst_node)) {
             ovsdb_weak_ref_destroy(weak);
         }
     }
-    LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->added_refs) {
+    LIST_FOR_EACH_SAFE (weak, src_node, &txn_row->added_refs) {
         ovs_list_remove(&weak->src_node);
         ovs_list_init(&weak->src_node);
         if (hmap_node_is_null(&weak->dst_node)) {
@@ -508,11 +508,11 @@ static struct ovsdb_error *
 ovsdb_txn_update_weak_refs(struct ovsdb_txn *txn OVS_UNUSED,
                            struct ovsdb_txn_row *txn_row)
 {
-    struct ovsdb_weak_ref *weak, *next, *dst_weak;
+    struct ovsdb_weak_ref *weak, *dst_weak;
     struct ovsdb_row *dst_row;
 
     /* Find and clean up deleted references from destination rows. */
-    LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->deleted_refs) {
+    LIST_FOR_EACH_SAFE (weak, src_node, &txn_row->deleted_refs) {
         dst_row = CONST_CAST(struct ovsdb_row *,
                     ovsdb_table_get_row(weak->dst_table, &weak->dst));
         if (dst_row) {
@@ -529,7 +529,7 @@ ovsdb_txn_update_weak_refs(struct ovsdb_txn *txn OVS_UNUSED,
     }
 
     /* Insert the weak references added in the new version of the row. */
-    LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->added_refs) {
+    LIST_FOR_EACH_SAFE (weak, src_node, &txn_row->added_refs) {
         dst_row = CONST_CAST(struct ovsdb_row *,
                     ovsdb_table_get_row(weak->dst_table, &weak->dst));
 
@@ -597,7 +597,7 @@ find_and_add_weak_ref(struct ovsdb_txn_row *txn_row,
 static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
 assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
 {
-    struct ovsdb_weak_ref *weak, *next;
+    struct ovsdb_weak_ref *weak;
     struct ovsdb_table *table;
     struct shash_node *node;
 
@@ -642,7 +642,7 @@ assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
 
         /* Collecting all key-value pairs that references deleted rows. */
         ovsdb_datum_init_empty(&deleted_refs);
-        LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->deleted_refs) {
+        LIST_FOR_EACH_SAFE (weak, src_node, &txn_row->deleted_refs) {
             if (column->index == weak->column_idx) {
                 ovsdb_datum_add_unsafe(&deleted_refs, &weak->key, &weak->value,
                                        &column->type, NULL);
@@ -1094,10 +1094,10 @@ static void
 ovsdb_txn_destroy_cloned(struct ovsdb_txn *txn)
 {
     ovs_assert(!txn->db);
-    struct ovsdb_txn_table *t, *next_txn_table;
-    LIST_FOR_EACH_SAFE (t, next_txn_table, node, &txn->txn_tables) {
-        struct ovsdb_txn_row *r, *next_txn_row;
-        HMAP_FOR_EACH_SAFE (r, next_txn_row, hmap_node, &t->txn_rows) {
+    struct ovsdb_txn_table *t;
+    LIST_FOR_EACH_SAFE (t, node, &txn->txn_tables) {
+        struct ovsdb_txn_row *r;
+        HMAP_FOR_EACH_SAFE (r, hmap_node, &t->txn_rows) {
             if (r->old) {
                 ovsdb_row_destroy(r->old);
             }
@@ -1549,19 +1549,19 @@ for_each_txn_row(struct ovsdb_txn *txn,
     serial++;
 
     do {
-        struct ovsdb_txn_table *t, *next_txn_table;
+        struct ovsdb_txn_table *t;
 
         any_work = false;
-        LIST_FOR_EACH_SAFE (t, next_txn_table, node, &txn->txn_tables) {
+        LIST_FOR_EACH_SAFE (t, node, &txn->txn_tables) {
             if (t->serial != serial) {
                 t->serial = serial;
                 t->n_processed = 0;
             }
 
             while (t->n_processed < hmap_count(&t->txn_rows)) {
-                struct ovsdb_txn_row *r, *next_txn_row;
+                struct ovsdb_txn_row *r;
 
-                HMAP_FOR_EACH_SAFE (r, next_txn_row, hmap_node, &t->txn_rows) {
+                HMAP_FOR_EACH_SAFE (r, hmap_node, &t->txn_rows) {
                     if (r->serial != serial) {
                         struct ovsdb_error *error;
 
@@ -1629,8 +1629,8 @@ ovsdb_txn_history_destroy(struct ovsdb *db)
         return;
     }
 
-    struct ovsdb_txn_history_node *txn_h_node, *next;
-    LIST_FOR_EACH_SAFE (txn_h_node, next, node, &db->txn_history) {
+    struct ovsdb_txn_history_node *txn_h_node;
+    LIST_FOR_EACH_SAFE (txn_h_node, node, &db->txn_history) {
         ovs_list_remove(&txn_h_node->node);
         ovsdb_txn_destroy_cloned(txn_h_node->txn);
         free(txn_h_node);
diff --git a/ovsdb/trigger.c b/ovsdb/trigger.c
index 726c138bf0..7d3003bca3 100644
--- a/ovsdb/trigger.c
+++ b/ovsdb/trigger.c
@@ -146,14 +146,14 @@ ovsdb_trigger_prereplace_db(struct ovsdb_trigger *trigger)
 bool
 ovsdb_trigger_run(struct ovsdb *db, long long int now)
 {
-    struct ovsdb_trigger *t, *next;
+    struct ovsdb_trigger *t;
 
     bool run_triggers = db->run_triggers;
     db->run_triggers_now = db->run_triggers = false;
 
     bool disconnect_all = false;
 
-    LIST_FOR_EACH_SAFE (t, next, node, &db->triggers) {
+    LIST_FOR_EACH_SAFE (t, node, &db->triggers) {
         if (run_triggers
             || now - t->created >= t->timeout_msec
             || t->progress || t->txn_forward) {
diff --git a/python/ovs/db/idl.py b/python/ovs/db/idl.py
index 4ecdcaa197..b87099ff52 100644
--- a/python/ovs/db/idl.py
+++ b/python/ovs/db/idl.py
@@ -140,6 +140,47 @@ class ConditionState(object):
         return False
 
 
+class IdlTable(object):
+    def __init__(self, idl, table):
+        assert(isinstance(table, ovs.db.schema.TableSchema))
+        self._table = table
+        self.need_table = False
+        self.rows = custom_index.IndexedRows(self)
+        self.idl = idl
+        self._condition_state = ConditionState()
+        self.columns = {k: IdlColumn(v) for k, v in table.columns.items()}
+
+    def __getattr__(self, attr):
+        return getattr(self._table, attr)
+
+    @property
+    def condition_state(self):
+        # read-only, no setter
+        return self._condition_state
+
+    @property
+    def condition(self):
+        return self.condition_state.latest
+
+    @condition.setter
+    def condition(self, condition):
+        assert(isinstance(condition, list))
+        self.idl.cond_change(self.name, condition)
+
+    @classmethod
+    def schema_tables(cls, idl, schema):
+        return {k: cls(idl, v) for k, v in schema.tables.items()}
+
+
+class IdlColumn(object):
+    def __init__(self, column):
+        self._column = column
+        self.alert = True
+
+    def __getattr__(self, attr):
+        return getattr(self._column, attr)
+
+
 class Idl(object):
     """Open vSwitch Database Interface Definition Language (OVSDB IDL).
 
@@ -241,7 +282,7 @@ class Idl(object):
         assert isinstance(schema_helper, SchemaHelper)
         schema = schema_helper.get_idl_schema()
 
-        self.tables = schema.tables
+        self.tables = IdlTable.schema_tables(self, schema)
         self.readonly = schema.readonly
         self._db = schema
         remotes = self._parse_remotes(remote)
@@ -282,15 +323,6 @@ class Idl(object):
         self.cond_changed = False
         self.cond_seqno = 0
 
-        for table in schema.tables.values():
-            for column in table.columns.values():
-                if not hasattr(column, 'alert'):
-                    column.alert = True
-            table.need_table = False
-            table.rows = custom_index.IndexedRows(table)
-            table.idl = self
-            table.condition = ConditionState()
-
     def _parse_remotes(self, remote):
         # If remote is -
         # "tcp:10.0.0.1:6641,unix:/tmp/db.sock,t,s,tcp:10.0.0.2:6642"
@@ -330,7 +362,7 @@ class Idl(object):
     def ack_conditions(self):
         """Mark all requested table conditions as acked"""
         for table in self.tables.values():
-            table.condition.ack()
+            table.condition_state.ack()
 
     def sync_conditions(self):
         """Synchronize condition state when the FSM is restarted
@@ -356,14 +388,17 @@ class Idl(object):
         flushing the local cached DB contents.
         """
         ack_all = self.last_id == str(uuid.UUID(int=0))
+        if ack_all:
+            self.cond_changed = False
+
         for table in self.tables.values():
             if ack_all:
-                table.condition.request()
-                table.condition.ack()
+                table.condition_state.request()
+                table.condition_state.ack()
             else:
-                if table.condition.reset():
+                if table.condition_state.reset():
                     self.last_id = str(uuid.UUID(int=0))
-                self.cond_changed = True
+                    self.cond_changed = True
 
     def restart_fsm(self):
         # Resync data DB table conditions to avoid missing updated due to
@@ -482,7 +517,7 @@ class Idl(object):
                     sh.register_table(self._server_db_table)
                     schema = sh.get_idl_schema()
                     self._server_db = schema
-                    self.server_tables = schema.tables
+                    self.server_tables = IdlTable.schema_tables(self, schema)
                     self.__send_server_monitor_request()
                 except error.Error as e:
                     vlog.err("%s: error receiving server schema: %s"
@@ -588,10 +623,10 @@ class Idl(object):
         for table in self.tables.values():
             # Always use the most recent conditions set by the IDL client when
             # requesting monitor_cond_change
-            if table.condition.new is not None:
+            if table.condition_state.new is not None:
                 change_requests[table.name] = [
-                    {"where": table.condition.new}]
-                table.condition.request()
+                    {"where": table.condition_state.new}]
+                table.condition_state.request()
 
         if not change_requests:
             return
@@ -627,19 +662,20 @@ class Idl(object):
             cond = [False]
 
         # Compare the new condition to the last known condition
-        if table.condition.latest != cond:
-            table.condition.init(cond)
+        if table.condition_state.latest != cond:
+            table.condition_state.init(cond)
             self.cond_changed = True
 
         # New condition will be sent out after all already requested ones
         # are acked.
-        if table.condition.new:
-            any_reqs = any(t.condition.request for t in self.tables.values())
+        if table.condition_state.new:
+            any_reqs = any(t.condition_state.request
+                           for t in self.tables.values())
             return self.cond_seqno + int(any_reqs) + 1
 
         # Already requested conditions should be up to date at
         # self.cond_seqno + 1 while acked conditions are already up to date
-        return self.cond_seqno + int(bool(table.condition.requested))
+        return self.cond_seqno + int(bool(table.condition_state.requested))
 
     def wait(self, poller):
         """Arranges for poller.block() to wake up when self.run() has something
@@ -811,8 +847,8 @@ class Idl(object):
                     columns.append(column)
             monitor_request = {"columns": columns}
             if method in ("monitor_cond", "monitor_cond_since") and (
-                    not ConditionState.is_true(table.condition.acked)):
-                monitor_request["where"] = table.condition.acked
+                    not ConditionState.is_true(table.condition_state.acked)):
+                monitor_request["where"] = table.condition_state.acked
             monitor_requests[table.name] = [monitor_request]
 
         args = [self._db.name, str(self.uuid), monitor_requests]
@@ -1148,13 +1184,6 @@ class Idl(object):
             return True
 
 
-def _uuid_to_row(atom, base):
-    if base.ref_table:
-        return base.ref_table.rows.get(atom)
-    else:
-        return atom
-
-
 def _row_to_uuid(value):
     if isinstance(value, Row):
         return value.uuid
@@ -1268,6 +1297,17 @@ class Row(object):
             data=", ".join("{col}={val}".format(col=c, val=getattr(self, c))
                            for c in sorted(self._table.columns)))
 
+    def _uuid_to_row(self, atom, base):
+        if base.ref_table:
+            try:
+                table = self._idl.tables[base.ref_table.name]
+            except KeyError as e:
+                msg = "Table {} is not registered".format(base.ref_table.name)
+                raise AttributeError(msg) from e
+            return table.rows.get(atom)
+        else:
+            return atom
+
     def __getattr__(self, column_name):
         assert self._changes is not None
         assert self._mutations is not None
@@ -1309,7 +1349,7 @@ class Row(object):
                     datum = data.Datum.from_python(column.type, dlist,
                                                    _row_to_uuid)
                 elif column.type.is_map():
-                    dmap = datum.to_python(_uuid_to_row)
+                    dmap = datum.to_python(self._uuid_to_row)
                     if inserts is not None:
                         dmap.update(inserts)
                     if removes is not None:
@@ -1326,7 +1366,7 @@ class Row(object):
                 else:
                     datum = inserts
 
-        return datum.to_python(_uuid_to_row)
+        return datum.to_python(self._uuid_to_row)
 
     def __setattr__(self, column_name, value):
         assert self._changes is not None
@@ -1410,7 +1450,7 @@ class Row(object):
         if value:
             try:
                 old_value = data.Datum.to_python(self._data[column_name],
-                                                 _uuid_to_row)
+                                                 self._uuid_to_row)
             except error.Error:
                 return
             if key not in old_value:
diff --git a/python/ovs/reconnect.py b/python/ovs/reconnect.py
index c4c6c87e9f..6b0d023ae3 100644
--- a/python/ovs/reconnect.py
+++ b/python/ovs/reconnect.py
@@ -44,7 +44,7 @@ class Reconnect(object):
         is_connected = False
 
         @staticmethod
-        def deadline(fsm):
+        def deadline(fsm, now):
             return None
 
         @staticmethod
@@ -56,7 +56,7 @@ class Reconnect(object):
         is_connected = False
 
         @staticmethod
-        def deadline(fsm):
+        def deadline(fsm, now):
             return None
 
         @staticmethod
@@ -68,7 +68,7 @@ class Reconnect(object):
         is_connected = False
 
         @staticmethod
-        def deadline(fsm):
+        def deadline(fsm, now):
             return fsm.state_entered + fsm.backoff
 
         @staticmethod
@@ -80,7 +80,7 @@ class Reconnect(object):
         is_connected = False
 
         @staticmethod
-        def deadline(fsm):
+        def deadline(fsm, now):
             return fsm.state_entered + max(1000, fsm.backoff)
 
         @staticmethod
@@ -92,13 +92,24 @@ class Reconnect(object):
         is_connected = True
 
         @staticmethod
-        def deadline(fsm):
+        def deadline(fsm, now):
             if fsm.probe_interval:
                 base = max(fsm.last_activity, fsm.state_entered)
                 expiration = base + fsm.probe_interval
-                if (fsm.last_receive_attempt is None or
+                if (now < expiration or
+                    fsm.last_receive_attempt is None or
                     fsm.last_receive_attempt >= expiration):
+                    # We still have time before the expiration or the time has
+                    # already passed and there was no activity.  In the first
+                    # case we need to wait for the expiration, in the second -
+                    # we're already past the deadline. */
                     return expiration
+                else:
+                    # Time has already passed, but we didn't attempt to receive
+                    # anything.  We need to wake up and try to receive even if
+                    # nothing is pending, so we can update the expiration time
+                    # or transition to a different state.
+                    return now + 1
             return None
 
         @staticmethod
@@ -114,12 +125,15 @@ class Reconnect(object):
         is_connected = True
 
         @staticmethod
-        def deadline(fsm):
+        def deadline(fsm, now):
             if fsm.probe_interval:
                 expiration = fsm.state_entered + fsm.probe_interval
-                if (fsm.last_receive_attempt is None or
+                if (now < expiration or
+                    fsm.last_receive_attempt is None or
                     fsm.last_receive_attempt >= expiration):
                     return expiration
+                else:
+                    return now + 1
             return None
 
         @staticmethod
@@ -134,7 +148,7 @@ class Reconnect(object):
         is_connected = False
 
         @staticmethod
-        def deadline(fsm):
+        def deadline(fsm, now):
             return fsm.state_entered
 
         @staticmethod
@@ -545,7 +559,7 @@ class Reconnect(object):
               returned if the "probe interval" is nonzero--see
               self.set_probe_interval())."""
 
-        deadline = self.state.deadline(self)
+        deadline = self.state.deadline(self, now)
         if deadline is not None and now >= deadline:
             return self.state.run(self, now)
         else:
@@ -562,7 +576,7 @@ class Reconnect(object):
         """Returns the number of milliseconds after which self.run() should be
         called if nothing else notable happens in the meantime, or None if this
         is currently unnecessary."""
-        deadline = self.state.deadline(self)
+        deadline = self.state.deadline(self, now)
         if deadline is not None:
             remaining = deadline - now
             return max(0, remaining)
diff --git a/tests/alb.at b/tests/alb.at
index 2bef06f39c..0036bd1f29 100644
--- a/tests/alb.at
+++ b/tests/alb.at
@@ -96,6 +96,52 @@ OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ALB - cross-numa])
+OVS_VSWITCHD_START([add-port br0 p0 \
+                    -- set Interface p0 type=dummy-pmd options:n_rxq=4 \
+                    -- set Interface p0 options:numa_id=0 \
+                    -- set Open_vSwitch . other_config:pmd-cpu-mask=0x3 \
+                    -- set open_vswitch . other_config:pmd-rxq-assign=group \
+                    -- set open_vswitch . other_config:pmd-rxq-isolate=false \
+                    -- set open_vswitch . other_config:pmd-auto-lb="true" \
+                    -- set open_vswitch . other_config:pmd-auto-lb-load-threshold=0],
+                   [], [], [--dummy-numa 1,2,1,2])
+OVS_WAIT_UNTIL([grep "PMD auto load balance is enabled" ovs-vswitchd.log])
+AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg])
+
+# no pinned rxqs - cross-numa pmd could change
+get_log_next_line_num
+ovs-appctl time/warp 600000 10000
+OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."])
+OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance detected cross-numa polling"])
+
+# all pinned rxqs - cross-numa pmd will not change
+AT_CHECK([ovs-vsctl set Interface p0 other_config:pmd-rxq-affinity='0:0,1:0,2:1,3:1'])
+get_log_next_line_num
+ovs-appctl time/warp 600000 10000
+OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."])
+OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "Variance improvement 0%."])
+
+# mix of pinned (non-isolated) and non-pinned rxqs - cross-numa pmd could change
+AT_CHECK([ovs-vsctl remove Interface p0 other_config pmd-rxq-affinity])
+AT_CHECK([ovs-vsctl set Interface p0 other_config:pmd-rxq-affinity='0:0,1:0,2:1'])
+get_log_next_line_num
+ovs-appctl time/warp 600000 10000
+OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."])
+OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance detected cross-numa polling"])
+
+# mix of pinned (isolated) and non-pinned rxqs - cross-numa pmd could change
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0xf])
+AT_CHECK([ovs-vsctl set Interface p0 options:n_rxq=6])
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-isolate=true])
+get_log_next_line_num
+ovs-appctl time/warp 600000 10000
+OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance performing dry run."])
+OVS_WAIT_UNTIL([tail -n +$LINENUM ovs-vswitchd.log | grep "PMD auto load balance detected cross-numa polling"])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ALB - PMD/RxQ assignment type])
 OVS_VSWITCHD_START([add-port br0 p0 \
                     -- set Interface p0 type=dummy-pmd options:n_rxq=3 \
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 7c2edeb9d4..ffb7208c7f 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -81,11 +81,12 @@ recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=ff:
 
 ovs-appctl netdev-dummy/set-admin-state p1 up
 ovs-appctl time/warp 100
-OVS_WAIT_UNTIL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [0], [dnl
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl
 ---- bond0 ----
 bond_mode: active-backup
 bond may use recirculation: no, <del>
 bond-hash-basis: 0
+lb_output action: disabled, bond-id: -1
 updelay: 0 ms
 downdelay: 0 ms
 lacp_status: off
@@ -99,7 +100,6 @@ member p1: enabled
 
 member p2: enabled
   may_enable: true
-
 ])
 
 OVS_VSWITCHD_STOP
@@ -129,11 +129,12 @@ ovs-appctl time/warp 100
 OVS_WAIT_UNTIL([test -n "`ovs-appctl bond/show | fgrep 'member p1: disabled'`"])
 ovs-appctl netdev-dummy/set-admin-state p1 up
 ovs-appctl time/warp 100
-OVS_WAIT_UNTIL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [0], [dnl
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl
 ---- bond0 ----
 bond_mode: active-backup
 bond may use recirculation: no, <del>
 bond-hash-basis: 0
+lb_output action: disabled, bond-id: -1
 updelay: 0 ms
 downdelay: 0 ms
 lacp_status: off
@@ -150,7 +151,6 @@ member p2: enabled
 
 member p3: enabled
   may_enable: true
-
 ])
 
 dnl Now delete the primary and verify that the output shows that the
@@ -171,11 +171,12 @@ ovs-vsctl \
    --id=@p1 create Interface name=p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock ofport_request=1 -- \
    set Port bond0 interfaces="$uuids, @p1]"
 ovs-appctl time/warp 100
-OVS_WAIT_UNTIL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [0], [dnl
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl
 ---- bond0 ----
 bond_mode: active-backup
 bond may use recirculation: no, <del>
 bond-hash-basis: 0
+lb_output action: disabled, bond-id: -1
 updelay: 0 ms
 downdelay: 0 ms
 lacp_status: off
@@ -192,17 +193,17 @@ member p2: enabled
 
 member p3: enabled
   may_enable: true
-
 ])
 
 dnl Switch to another primary
 ovs-vsctl set port bond0 other_config:bond-primary=p2
 ovs-appctl time/warp 100
-OVS_WAIT_UNTIL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [0], [dnl
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl
 ---- bond0 ----
 bond_mode: active-backup
 bond may use recirculation: no, <del>
 bond-hash-basis: 0
+lb_output action: disabled, bond-id: -1
 updelay: 0 ms
 downdelay: 0 ms
 lacp_status: off
@@ -211,25 +212,25 @@ active-backup primary: p2
 <active member mac del>
 
 member p1: enabled
-  active member
   may_enable: true
 
 member p2: enabled
+  active member
   may_enable: true
 
 member p3: enabled
   may_enable: true
-
 ])
 
 dnl Remove the "bond-primary" config directive from the bond.
 AT_CHECK([ovs-vsctl remove Port bond0 other_config bond-primary])
 ovs-appctl time/warp 100
-OVS_WAIT_UNTIL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [0], [dnl
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl bond/show | STRIP_RECIRC_ID | STRIP_ACTIVE_MEMBER_MAC], [dnl
 ---- bond0 ----
 bond_mode: active-backup
 bond may use recirculation: no, <del>
 bond-hash-basis: 0
+lb_output action: disabled, bond-id: -1
 updelay: 0 ms
 downdelay: 0 ms
 lacp_status: off
@@ -238,15 +239,14 @@ active-backup primary: <none>
 <active member mac del>
 
 member p1: enabled
-  active member
   may_enable: true
 
 member p2: enabled
+  active member
   may_enable: true
 
 member p3: enabled
   may_enable: true
-
 ])
 
 OVS_VSWITCHD_STOP
@@ -5573,7 +5573,36 @@ check_flows () {
     echo "n_packets=$n"
     test "$n" = 1
 }
-OVS_WAIT_UNTIL([check_flows], [ovs dump-flows br0])
+OVS_WAIT_UNTIL([check_flows], [ovs-ofctl dump-flows br0])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+# Checks for regression against a bug in which OVS crashed
+# with in_port=OFPP_NONE or in_port=OFPP_CONTROLLER and
+# recirculation is involved.
+AT_SETUP([ofproto-dpif - packet-out recirculation with OFPP_NONE and OFPP_CONTROLLER])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2
+
+AT_DATA([flows.txt], [dnl
+table=0 ip actions=mod_dl_dst:83:83:83:83:83:83,ct(table=1)
+table=1 ip actions=ct(commit),normal
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+packet=ffffffffffff00102030405008004500001c00000000401100000a000002ffffffff0035111100080000
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=none,packet=$packet actions=table"])
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller,packet=$packet actions=table"])
+
+# Dumps out the flow table, extracts the number of packets that have gone
+# through the (single) flow in table 1, and returns success if it's exactly 2.
+check_flows () {
+    n=$(ovs-ofctl dump-flows br0 table=1 | sed -n 's/.*n_packets=\([[0-9]]\{1,\}\).*/\1/p')
+    echo "n_packets=$n"
+    test "$n" = 2
+}
+OVS_WAIT_UNTIL([check_flows], [ovs-ofctl dump-flows br0])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
index 736d9809cb..7051d95396 100644
--- a/tests/ofproto-macros.at
+++ b/tests/ofproto-macros.at
@@ -175,6 +175,7 @@ m4_define([_OVS_VSWITCHD_START],
 /dpdk|INFO|DPDK Disabled - Use other_config:dpdk-init to enable/d
 /netlink_socket|INFO|netlink: could not enable listening to all nsid/d
 /probe tc:/d
+/setting extended ack support failed/d
 /tc: Using policy/d']])
 ])
 
@@ -239,6 +240,7 @@ check_logs () {
 /timeval.*context switches: [[0-9]]* voluntary, [[0-9]]* involuntary/d
 /ovs_rcu.*blocked [[0-9]]* ms waiting for .* to quiesce/d
 /Dropped [[0-9]]* log messages/d
+/setting extended ack support failed/d
 /|WARN|/p
 /|ERR|/p
 /|EMER|/p" ${logs}
diff --git a/tests/ovs-macros.at b/tests/ovs-macros.at
index 66545da572..e6c5bc6e94 100644
--- a/tests/ovs-macros.at
+++ b/tests/ovs-macros.at
@@ -259,7 +259,20 @@ dnl Executes shell COMMAND in a loop until it returns zero.  If COMMAND does
 dnl not return zero within a reasonable time limit, executes the commands
 dnl in IF-FAILED (if provided) and fails the test.
 m4_define([OVS_WAIT_UNTIL],
-  [OVS_WAIT([$1], [$2], [AT_LINE], [until $1])])
+  [AT_FAIL_IF([test "$#" -ge 3])
+   dnl The second argument should not be a number (confused with AT_CHECK ?).
+   AT_FAIL_IF([test "$#" -eq 2 && test "$2" -eq "$2" 2>/dev/null])
+   OVS_WAIT([$1], [$2], [AT_LINE], [until $1])])
+
+dnl OVS_WAIT_UNTIL_EQUAL(COMMAND, OUTPUT)
+dnl
+dnl Executes shell COMMAND in a loop until it returns zero and the output
+dnl equals OUTPUT.  If COMMAND does not return zero or a desired output within
+dnl a reasonable time limit, fails the test.
+m4_define([OVS_WAIT_UNTIL_EQUAL],
+  [AT_FAIL_IF([test "$#" -ge 3])
+   echo "$2" > wait_until_expected
+   OVS_WAIT_UNTIL([$1 | diff -u wait_until_expected - ])])
 
 dnl OVS_WAIT_WHILE(COMMAND, [IF-FAILED])
 dnl
@@ -267,7 +280,10 @@ dnl Executes shell COMMAND in a loop until it returns nonzero.  If COMMAND does
 dnl not return nonzero within a reasonable time limit, executes the commands
 dnl in IF-FAILED (if provided) and fails the test.
 m4_define([OVS_WAIT_WHILE],
-  [OVS_WAIT([if $1; then return 1; else return 0; fi], [$2],
+  [AT_FAIL_IF([test "$#" -ge 3])
+   dnl The second argument should not be a number (confused with AT_CHECK ?).
+   AT_FAIL_IF([test "$#" -eq 2 && test "$2" -eq "$2" 2>/dev/null])
+   OVS_WAIT([if $1; then return 1; else return 0; fi], [$2],
             [AT_LINE], [while $1])])
 
 dnl OVS_APP_EXIT_AND_WAIT(DAEMON)
diff --git a/tests/ovs-vswitchd.at b/tests/ovs-vswitchd.at
index bba4fea2bc..977b2eba1f 100644
--- a/tests/ovs-vswitchd.at
+++ b/tests/ovs-vswitchd.at
@@ -121,6 +121,7 @@ OVS_APP_EXIT_AND_WAIT_BY_TARGET(["`pwd`"/unixctl2], [ovs-vswitchd-2.pid])
 # the process.
 AT_CHECK([sed -n "
 /|ERR|another ovs-vswitchd process is running/d
+/setting extended ack support failed/d
 /|WARN|/p
 /|ERR|/p
 /|EMER|/p" fakelog
@@ -148,6 +149,7 @@ AT_CHECK([grep "wakeup due to" ovs-vswitchd.log], [ignore])
 
 # check the log, should not see any WARN/ERR/EMER log.
 AT_CHECK([sed -n "
+/setting extended ack support failed/d
 /|WARN|/p
 /|ERR|/p
 /|EMER|/p" ovs-vswitchd.log
diff --git a/tests/ovsdb-cluster.at b/tests/ovsdb-cluster.at
index fc6253cfe9..ee9c7b9379 100644
--- a/tests/ovsdb-cluster.at
+++ b/tests/ovsdb-cluster.at
@@ -400,6 +400,61 @@ done
 
 AT_CLEANUP
 
+AT_BANNER([OVSDB - cluster failure while joining])
+AT_SETUP([OVSDB cluster - follower crash while joining])
+AT_KEYWORDS([ovsdb server negative unix cluster join])
+
+n=3
+schema_name=`ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema`
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db dnl
+              $abs_srcdir/idltest.ovsschema unix:s1.raft], [0], [], [stderr])
+cid=`ovsdb-tool db-cid s1.db`
+schema_name=`ovsdb-tool schema-name $abs_srcdir/idltest.ovsschema`
+for i in `seq 2 $n`; do
+    AT_CHECK([ovsdb-tool join-cluster s$i.db $schema_name unix:s$i.raft unix:s1.raft])
+done
+
+on_exit 'kill `cat *.pid`'
+
+dnl Starting followers first, so we can configure them to crash on join.
+for j in `seq $n`; do
+    i=$(($n + 1 - $j))
+    AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off dnl
+                           --detach --no-chdir --log-file=s$i.log dnl
+                           --pidfile=s$i.pid --unixctl=s$i dnl
+                           --remote=punix:s$i.ovsdb s$i.db])
+    if test $i != 1; then
+        OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/s$i dnl
+                            cluster/failure-test crash-before-sending-install-snapshot-reply dnl
+                            | grep -q "engaged"])
+    fi
+done
+
+dnl Make sure that followers really crashed.
+for i in `seq 2 $n`; do
+    OVS_WAIT_WHILE([test -s s$i.pid])
+done
+
+dnl Bring them back.
+for i in `seq 2 $n`; do
+    AT_CHECK([ovsdb-server -v -vconsole:off -vsyslog:off dnl
+                           --detach --no-chdir --log-file=s$i.log dnl
+                           --pidfile=s$i.pid --unixctl=s$i dnl
+                           --remote=punix:s$i.ovsdb s$i.db])
+done
+
+dnl Make sure that all servers joined the cluster.
+for i in `seq $n`; do
+    AT_CHECK([ovsdb_client_wait unix:s$i.ovsdb $schema_name connected])
+done
+
+for i in `seq $n`; do
+    OVS_APP_EXIT_AND_WAIT_BY_TARGET([`pwd`/s$i], [s$i.pid])
+done
+
+AT_CLEANUP
+
 
 
 OVS_START_SHELL_HELPERS
@@ -416,9 +471,8 @@ ovsdb_cluster_failure_test () {
 
     cp $top_srcdir/vswitchd/vswitch.ovsschema schema
     schema=`ovsdb-tool schema-name schema`
-    AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db schema unix:s1.raft], [0], [], [dnl
-ovsdb|WARN|schema: changed 30 columns in 'Open_vSwitch' database from ephemeral to persistent, including 'status' column in 'Manager' table, because clusters do not support ephemeral columns
-])
+    AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db schema unix:s1.raft], [0], [], [stderr])
+    AT_CHECK([sed < stderr "/ovsdb|WARN|schema: changed .* columns in 'Open_vSwitch' database from ephemeral to persistent/d"])
 
     n=3
     join_cluster() {
@@ -629,9 +683,8 @@ ovsdb_torture_test () {
     local variant=$3            # 'kill' and restart or 'remove' and add
     cp $top_srcdir/vswitchd/vswitch.ovsschema schema
     schema=`ovsdb-tool schema-name schema`
-    AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db schema unix:s1.raft], [0], [], [dnl
-ovsdb|WARN|schema: changed 30 columns in 'Open_vSwitch' database from ephemeral to persistent, including 'status' column in 'Manager' table, because clusters do not support ephemeral columns
-])
+    AT_CHECK([ovsdb-tool '-vPATTERN:console:%c|%p|%m' create-cluster s1.db schema unix:s1.raft], [0], [], [stderr])
+    AT_CHECK([sed < stderr "/ovsdb|WARN|schema: changed .* columns in 'Open_vSwitch' database from ephemeral to persistent/d"])
 
     join_cluster() {
         local i=$1
diff --git a/tests/pmd.at b/tests/pmd.at
index a2f9d34a2a..0a451f33c6 100644
--- a/tests/pmd.at
+++ b/tests/pmd.at
@@ -199,7 +199,7 @@ pmd thread numa_id <cleared> core_id <cleared>:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([PMD - pmd-cpu-mask - NUMA])
+AT_SETUP([PMD - pmd-cpu-mask - dual NUMA])
 OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy-pmd options:n_rxq=8 options:numa_id=1 -- set Open_vSwitch . other_config:pmd-cpu-mask=1],
                    [], [], [--dummy-numa 1,1,0,0])
 
@@ -359,6 +359,44 @@ pmd thread numa_id 1 core_id 0:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([PMD - pmd-cpu-mask - multi NUMA])
+OVS_VSWITCHD_START([add-port br0 p0 \
+                    -- set Interface p0 type=dummy-pmd options:n_rxq=4 \
+                    -- set Interface p0 options:numa_id=0 \
+                    -- set Open_vSwitch . other_config:pmd-cpu-mask=0xf \
+                    -- set open_vswitch . other_config:pmd-rxq-assign=cycles],
+                   [], [], [--dummy-numa 1,2,1,2])
+
+TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1))
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=group])
+
+OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using group algorithm"])
+OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 0."])
+
+# check all pmds from both non-local numas are assigned an rxq
+AT_CHECK([test `ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | wc -l` -eq 4])
+
+TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1))
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=cycles])
+
+OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using cycles algorithm"])
+OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 0."])
+
+# check all pmds from both non-local numas are assigned an rxq
+AT_CHECK([test `ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | wc -l` -eq 4])
+
+TMP=$(($(cat ovs-vswitchd.log | wc -l | tr -d [[:blank:]])+1))
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:pmd-rxq-assign=roundrobin])
+
+OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "Performing pmd to rx queue assignment using roundrobin algorithm"])
+OVS_WAIT_UNTIL([tail -n +$TMP ovs-vswitchd.log | grep "There's no available (non-isolated) pmd thread on numa node 0."])
+
+# check all pmds from both non-local numas are assigned an rxq
+AT_CHECK([test `ovs-appctl dpif-netdev/pmd-rxq-show | awk '/AVAIL$/ { printf("%s\t", $0); next } 1' | parse_pmd_rxq_show_group | wc -l` -eq 4])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([PMD - stats])
 OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 ofport_request=7 type=dummy-pmd options:n_rxq=4],
                    [], [], [DUMMY_NUMA])
diff --git a/tests/reconnect.at b/tests/reconnect.at
index 0f74709f5a..5bca84351c 100644
--- a/tests/reconnect.at
+++ b/tests/reconnect.at
@@ -39,8 +39,19 @@ run
 connected
 
 # Try timeout without noting that we tried to receive.
-# (This does nothing since we never timeout in this case.)
+# Timeout should be scheduled to the next probe interval.
 timeout
+run
+
+# Once we reached the timeout, it should not expire until the receive actually
+# attempted.  However, we still need to wake up as soon as possible in order to
+# have a chance to mark the receive attempt even if nothing was received.
+timeout
+run
+
+# Short time advance past the original probe interval, but not expired still.
+timeout
+run
 
 # Now disable the receive-attempted feature and timeout again.
 receive-attempted LLONG_MAX
@@ -67,18 +78,37 @@ connected
   last connected 0 ms ago, connected 0 ms total
 
 # Try timeout without noting that we tried to receive.
-# (This does nothing since we never timeout in this case.)
-timeout
-  no timeout
-
-# Now disable the receive-attempted feature and timeout again.
-receive-attempted LLONG_MAX
+# Timeout should be scheduled to the next probe interval.
 timeout
   advance 5000 ms
 
 ### t=6000 ###
   in ACTIVE for 5000 ms (0 ms backoff)
 run
+
+# Once we reached the timeout, it should not expire until the receive actually
+# attempted.  However, we still need to wake up as soon as possible in order to
+# have a chance to mark the receive attempt even if nothing was received.
+timeout
+  advance 1 ms
+
+### t=6001 ###
+  in ACTIVE for 5001 ms (0 ms backoff)
+run
+
+# Short time advance past the original probe interval, but not expired still.
+timeout
+  advance 1 ms
+
+### t=6002 ###
+  in ACTIVE for 5002 ms (0 ms backoff)
+run
+
+# Now disable the receive-attempted feature and timeout again.
+receive-attempted LLONG_MAX
+timeout
+  advance 0 ms
+run
   should send probe
   in IDLE for 0 ms (0 ms backoff)
 
@@ -86,7 +116,7 @@ run
 timeout
   advance 5000 ms
 
-### t=11000 ###
+### t=11002 ###
   in IDLE for 5000 ms (0 ms backoff)
 run
   should disconnect
@@ -94,7 +124,7 @@ disconnected
   in BACKOFF for 0 ms (1000 ms backoff)
   1 successful connections out of 1 attempts, seqno 2
   disconnected
-  disconnected at 11000 ms (0 ms ago)
+  disconnected at 11002 ms (0 ms ago)
 ])
 
 ######################################################################
@@ -111,8 +141,19 @@ run
 connected
 
 # Try timeout without noting that we tried to receive.
-# (This does nothing since we never timeout in this case.)
+# Timeout should be scheduled to the next probe interval.
+timeout
+run
+
+# Once we reached the timeout, it should not expire until the receive actually
+# attempted.  However, we still need to wake up as soon as possible in order to
+# have a chance to mark the receive attempt even if nothing was received.
+timeout
+run
+
+# Short time advance past the original probe interval, but not expired still.
 timeout
+run
 
 # Now disable the receive-attempted feature and timeout again.
 receive-attempted LLONG_MAX
@@ -148,18 +189,37 @@ connected
   last connected 0 ms ago, connected 0 ms total
 
 # Try timeout without noting that we tried to receive.
-# (This does nothing since we never timeout in this case.)
-timeout
-  no timeout
-
-# Now disable the receive-attempted feature and timeout again.
-receive-attempted LLONG_MAX
+# Timeout should be scheduled to the next probe interval.
 timeout
   advance 5000 ms
 
 ### t=6500 ###
   in ACTIVE for 5000 ms (0 ms backoff)
 run
+
+# Once we reached the timeout, it should not expire until the receive actually
+# attempted.  However, we still need to wake up as soon as possible in order to
+# have a chance to mark the receive attempt even if nothing was received.
+timeout
+  advance 1 ms
+
+### t=6501 ###
+  in ACTIVE for 5001 ms (0 ms backoff)
+run
+
+# Short time advance past the original probe interval, but not expired still.
+timeout
+  advance 1 ms
+
+### t=6502 ###
+  in ACTIVE for 5002 ms (0 ms backoff)
+run
+
+# Now disable the receive-attempted feature and timeout again.
+receive-attempted LLONG_MAX
+timeout
+  advance 0 ms
+run
   should send probe
   in IDLE for 0 ms (0 ms backoff)
 
@@ -167,7 +227,7 @@ run
 timeout
   advance 5000 ms
 
-### t=11500 ###
+### t=11502 ###
   in IDLE for 5000 ms (0 ms backoff)
 run
   should disconnect
@@ -175,7 +235,7 @@ disconnected
   in BACKOFF for 0 ms (1000 ms backoff)
   1 successful connections out of 1 attempts, seqno 2
   disconnected
-  disconnected at 11500 ms (0 ms ago)
+  disconnected at 11502 ms (0 ms ago)
 ])
 
 ######################################################################
@@ -1271,14 +1331,14 @@ activity
   created 1000, last activity 3000, last connected 2000
 
 # Connection times out.
-timeout
-  no timeout
-receive-attempted LLONG_MAX
 timeout
   advance 5000 ms
 
 ### t=8000 ###
   in ACTIVE for 6000 ms (1000 ms backoff)
+receive-attempted LLONG_MAX
+timeout
+  advance 0 ms
 run
   should send probe
   in IDLE for 0 ms (1000 ms backoff)
diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at
index 19a0b125b9..8b9f5c7525 100644
--- a/tests/system-common-macros.at
+++ b/tests/system-common-macros.at
@@ -281,6 +281,14 @@ m4_define([OVS_START_L7],
 #
 m4_define([OFPROTO_CLEAR_DURATION_IDLE], [[sed -e 's/duration=.*s,/duration=<cleared>,/g' -e 's/idle_age=[0-9]*,/idle_age=<cleared>,/g']])
 
+# OVS_CHECK_TUNNEL_TSO()
+#
+# Macro to be used in general tunneling tests that could be also
+# used by system-tso. In that case, tunneling is not supported and
+# the test should be skipped.
+m4_define([OVS_CHECK_TUNNEL_TSO],
+    [m4_ifdef([CHECK_SYSTEM_TSO], [AT_SKIP_IF(:)])])
+
 # OVS_CHECK_VXLAN()
 #
 # Do basic check for vxlan functionality, skip the test if it's not there.
diff --git a/tests/system-dpdk.at b/tests/system-dpdk.at
index c3ee6990ca..7d2715c4a7 100644
--- a/tests/system-dpdk.at
+++ b/tests/system-dpdk.at
@@ -237,6 +237,10 @@ AT_CHECK([ovs-vsctl show], [], [stdout])
 AT_SKIP_IF([! ovs-appctl dpif-netdev/miniflow-parser-get | sed 1,4d | grep "True"], [], [dnl
 ])
 
+AT_CHECK([ovs-appctl dpif-netdev/dpif-impl-set dpif_avx512], [0], [dnl
+DPIF implementation set to dpif_avx512.
+])
+
 AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set autovalidator], [0], [dnl
 Miniflow extract implementation set to autovalidator.
 ])
@@ -265,6 +269,10 @@ AT_CHECK([ovs-vsctl show], [], [stdout])
 AT_SKIP_IF([! ovs-appctl dpif-netdev/miniflow-parser-get | sed 1,4d | grep "True"], [], [dnl
 ])
 
+AT_CHECK([ovs-appctl dpif-netdev/dpif-impl-set dpif_avx512], [0], [dnl
+DPIF implementation set to dpif_avx512.
+])
+
 AT_CHECK([ovs-appctl dpif-netdev/miniflow-parser-set autovalidator], [0], [dnl
 Miniflow extract implementation set to autovalidator.
 ])
diff --git a/tests/system-route.at b/tests/system-route.at
index 1714273e35..270956d13f 100644
--- a/tests/system-route.at
+++ b/tests/system-route.at
@@ -14,10 +14,9 @@ dnl Add ip address.
 AT_CHECK([ip addr add 10.0.0.17/24 dev p1-route], [0], [stdout])
 
 dnl Check that OVS catches route updates.
-OVS_WAIT_UNTIL([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [0], [dnl
-Cached: 10.0.0.17/24 dev p1-route SRC 10.0.0.17
-Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local
-])
+OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | grep 'p1-route' | sort], [dnl
+Cached: 10.0.0.0/24 dev p1-route SRC 10.0.0.17
+Cached: 10.0.0.17/32 dev p1-route SRC 10.0.0.17 local])
 
 dnl Delete ip address.
 AT_CHECK([ip addr del 10.0.0.17/24 dev p1-route], [0], [stdout])
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index f22d86e466..1d20366280 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -218,6 +218,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over vxlan tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_VXLAN()
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -258,7 +259,55 @@ NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PI
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([datapath - ping vlan over vxlan tunnel])
+OVS_CHECK_TUNNEL_TSO()
+OVS_CHECK_VXLAN()
+
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-underlay])
+
+AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"])
+AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"])
+
+ADD_NAMESPACES(at_ns0)
+
+dnl Set up underlay link from host into the namespace using veth pair.
+ADD_VETH(p0, at_ns0, br-underlay, "172.31.2.1/24")
+AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"])
+AT_CHECK([ip link set dev br-underlay up])
+
+dnl Set up tunnel endpoints on OVS outside the namespace and with a native
+dnl linux device inside the namespace.
+ADD_OVS_TUNNEL([vxlan], [br0], [at_vxlan0], [172.31.1.1], [10.1.1.100/24])
+ADD_NATIVE_TUNNEL([vxlan], [at_vxlan1], [at_ns0], [172.31.1.100], [10.2.1.1/24],
+                  [id 0 dstport 4789])
+
+AT_CHECK([ovs-vsctl set port br0 tag=100])
+AT_CHECK([ovs-vsctl set port br-underlay tag=42])
+
+ADD_VLAN(at_vxlan1, at_ns0, 100, "10.1.1.1/24")
+ADD_VLAN(p0, at_ns0, 42, "172.31.1.1/24")
+
+dnl First, check the underlay
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 172.31.1.100 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+dnl Okay, now check the overlay with different packet sizes
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([datapath - ping over vxlan6 tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_VXLAN_UDP6ZEROCSUM()
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -302,6 +351,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over gre tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15)
 OVS_CHECK_GRE()
 
@@ -343,6 +393,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over ip6gre L2 tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15)
 OVS_CHECK_GRE()
 OVS_CHECK_ERSPAN()
@@ -383,6 +434,7 @@ AT_CLEANUP
 
 
 AT_SETUP([datapath - ping over erspan v1 tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15)
 OVS_CHECK_GRE()
 OVS_CHECK_ERSPAN()
@@ -419,6 +471,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over erspan v2 tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15)
 OVS_CHECK_GRE()
 OVS_CHECK_ERSPAN()
@@ -455,6 +508,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over ip6erspan v1 tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15)
 OVS_CHECK_GRE()
 OVS_CHECK_ERSPAN()
@@ -494,6 +548,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over ip6erspan v2 tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_KERNEL_EXCL(3, 10, 4, 15)
 OVS_CHECK_GRE()
 OVS_CHECK_ERSPAN()
@@ -534,6 +589,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over geneve tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_GENEVE()
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -575,6 +631,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over geneve tunnel, delete flow regression])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_GENEVE()
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -629,6 +686,7 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/|ERR|/d
 AT_CLEANUP
 
 AT_SETUP([datapath - flow resume with geneve tun_metadata])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_GENEVE()
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -680,6 +738,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over geneve6 tunnel])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_GENEVE_UDP6ZEROCSUM()
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -723,6 +782,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over gre tunnel by simulated packets])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_MIN_KERNEL(3, 10)
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -769,6 +829,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over erspan v1 tunnel by simulated packets])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_MIN_KERNEL(3, 10)
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -817,6 +878,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over erspan v2 tunnel by simulated packets])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_MIN_KERNEL(3, 10)
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -870,6 +932,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over ip6erspan v1 tunnel by simulated packets])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_MIN_KERNEL(3, 10)
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -925,6 +988,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([datapath - ping over ip6erspan v2 tunnel by simulated packets])
+OVS_CHECK_TUNNEL_TSO()
 OVS_CHECK_MIN_KERNEL(3, 10)
 
 OVS_TRAFFIC_VSWITCHD_START()
@@ -4100,15 +4164,15 @@ action=normal
 
 AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
 
-AT_CHECK([ovs-ofctl packet-out br0 "packet=52540003287c525400444ab586dd6006f70605b02c4020010001000000000000000000000020200100010000000000000000000000101100000134e88deb13891389080803136161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=42,packet=52540003287c525400444ab586dd6006f70605b02c4020010001000000000000000000000020200100010000000000000000000000101100000134e88deb13891389080803136161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl
 "16161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161"dnl
 "61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl
 "1616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161, actions=ct(table=1)"])
 
-AT_CHECK([ovs-ofctl packet-out br0 "packet=52540003287c525400444ab586dd6006f70602682c402001000100000000000000000000002020010001000000000000000000000010110005a834e88deb6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=42,packet=52540003287c525400444ab586dd6006f70602682c402001000100000000000000000000002020010001000000000000000000000010110005a834e88deb6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl
 "161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161, actions=ct(table=1)"])
 
-AT_CHECK([ovs-ofctl packet-out br0 "packet=52540003287c525400444ab586dd6006f706033d1140200100010000000000000000000000202001000100000000000000000000001013891389033d923861616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=42,packet=52540003287c525400444ab586dd6006f706033d1140200100010000000000000000000000202001000100000000000000000000001013891389033d923861616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616"dnl
 "1616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161610a, actions=ct(table=1)"])
 
 AT_CHECK([ovs-appctl dpctl/dump-flows | head -2 | tail -1 | grep -q -e ["]udp[(]src=5001["]])
@@ -6454,7 +6518,7 @@ on_exit 'ovs-appctl revalidator/purge'
 on_exit 'ovs-appctl dpif/dump-flows br0'
 
 dnl Should work with the virtual IP address through NAT
-for i in 1 2 3 4 5 6 7 8 9 10 11 12; do
+for i in $(seq 1 50); do
     echo Request $i
     NS_CHECK_EXEC([at_ns1], [wget 10.1.1.64 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
 done
diff --git a/tests/system-tso-macros.at b/tests/system-tso-macros.at
index 406334f3e0..1a80047619 100644
--- a/tests/system-tso-macros.at
+++ b/tests/system-tso-macros.at
@@ -29,3 +29,5 @@ m4_define([CONFIGURE_VETH_OFFLOADS],
     [AT_CHECK([ethtool -K $1 sg on], [0], [ignore], [ignore])]
     [AT_CHECK([ethtool -K $1 tso on], [0], [ignore], [ignore])]
 )
+
+m4_define([CHECK_SYSTEM_TSO], [])
diff --git a/tests/test-cmap.c b/tests/test-cmap.c
index 0705475606..588a5dea63 100644
--- a/tests/test-cmap.c
+++ b/tests/test-cmap.c
@@ -74,6 +74,7 @@ check_cmap(struct cmap *cmap, const int values[], size_t n,
         cmap_values[i++] = e->value;
     }
     assert(i == n);
+    assert(e == NULL);
 
     /* Here we test iteration with cmap_next_position() */
     i = 0;
@@ -107,6 +108,7 @@ check_cmap(struct cmap *cmap, const int values[], size_t n,
             count += e->value == values[i];
         }
         assert(count == 1);
+        assert(e == NULL);
     }
 
     /* Check that all the values are there in batched lookup. */
@@ -130,6 +132,7 @@ check_cmap(struct cmap *cmap, const int values[], size_t n,
             CMAP_NODE_FOR_EACH (e, node, nodes[k]) {
                 count += e->value == values[i + k];
             }
+            assert(e == NULL);
         }
         assert(count == j); /* j elements in a batch. */
     }
@@ -584,7 +587,7 @@ benchmark_hmap(void)
 {
     struct helement *elements;
     struct hmap hmap;
-    struct helement *e, *next;
+    struct helement *e;
     struct timeval start;
     pthread_t *threads;
     struct hmap_aux aux;
@@ -622,7 +625,7 @@ benchmark_hmap(void)
 
     /* Destruction. */
     xgettimeofday(&start);
-    HMAP_FOR_EACH_SAFE (e, next, node, &hmap) {
+    HMAP_FOR_EACH_SAFE (e, node, &hmap) {
         hmap_remove(&hmap, &e->node);
     }
     hmap_destroy(&hmap);
diff --git a/tests/test-hindex.c b/tests/test-hindex.c
index af06be5fcc..cc2b1b8bd9 100644
--- a/tests/test-hindex.c
+++ b/tests/test-hindex.c
@@ -265,6 +265,43 @@ test_hindex_for_each_safe(hash_func *hash)
             i = 0;
             n_remaining = n;
             HINDEX_FOR_EACH_SAFE (e, next, node, &hindex) {
+                if (hindex_next(&hindex, &e->node) == NULL) {
+                    assert(next == NULL);
+                } else {
+                    assert(&next->node == hindex_next(&hindex, &e->node));
+                }
+                assert(i < n);
+                if (pattern & (1ul << e->value)) {
+                    size_t j;
+                    hindex_remove(&hindex, &e->node);
+                    for (j = 0; ; j++) {
+                        assert(j < n_remaining);
+                        if (values[j] == e->value) {
+                            values[j] = values[--n_remaining];
+                            break;
+                        }
+                    }
+                }
+                check_hindex(&hindex, values, n_remaining, hash);
+                i++;
+            }
+            assert(i == n);
+            assert(next == NULL);
+
+            for (i = 0; i < n; i++) {
+                if (pattern & (1ul << i)) {
+                    n_remaining++;
+                }
+            }
+            assert(n == n_remaining);
+            hindex_destroy(&hindex);
+
+            /* Test short version (without the next variable). */
+            make_hindex(&hindex, elements, values, n, hash);
+
+            i = 0;
+            n_remaining = n;
+            HINDEX_FOR_EACH_SAFE (e, node, &hindex) {
                 assert(i < n);
                 if (pattern & (1ul << e->value)) {
                     size_t j;
diff --git a/tests/test-hmap.c b/tests/test-hmap.c
index 9259b0b3fc..e50c7c3807 100644
--- a/tests/test-hmap.c
+++ b/tests/test-hmap.c
@@ -62,6 +62,7 @@ check_hmap(struct hmap *hmap, const int values[], size_t n,
         hmap_values[i++] = e->value;
     }
     assert(i == n);
+    assert(e == NULL);
 
     memcpy(sort_values, values, sizeof *sort_values * n);
     qsort(sort_values, n, sizeof *sort_values, compare_ints);
@@ -82,6 +83,7 @@ check_hmap(struct hmap *hmap, const int values[], size_t n,
             count += e->value == values[i];
         }
         assert(count == 1);
+        assert(e == NULL);
     }
 
     /* Check counters. */
@@ -243,6 +245,44 @@ test_hmap_for_each_safe(hash_func *hash)
             i = 0;
             n_remaining = n;
             HMAP_FOR_EACH_SAFE (e, next, node, &hmap) {
+                if (hmap_next(&hmap, &e->node) == NULL) {
+                    assert(next == NULL);
+                } else {
+                    assert(&next->node == hmap_next(&hmap, &e->node));
+                }
+                assert(i < n);
+                if (pattern & (1ul << e->value)) {
+                    size_t j;
+                    hmap_remove(&hmap, &e->node);
+                    for (j = 0; ; j++) {
+                        assert(j < n_remaining);
+                        if (values[j] == e->value) {
+                            values[j] = values[--n_remaining];
+                            break;
+                        }
+                    }
+                }
+                check_hmap(&hmap, values, n_remaining, hash);
+                i++;
+            }
+            assert(i == n);
+            assert(next == NULL);
+            assert(e == NULL);
+
+            for (i = 0; i < n; i++) {
+                if (pattern & (1ul << i)) {
+                    n_remaining++;
+                }
+            }
+            assert(n == n_remaining);
+            hmap_destroy(&hmap);
+
+            /* Test short version (without next variable). */
+            make_hmap(&hmap, elements, values, n, hash);
+
+            i = 0;
+            n_remaining = n;
+            HMAP_FOR_EACH_SAFE (e, node, &hmap) {
                 assert(i < n);
                 if (pattern & (1ul << e->value)) {
                     size_t j;
@@ -259,6 +299,7 @@ test_hmap_for_each_safe(hash_func *hash)
                 i++;
             }
             assert(i == n);
+            assert(e == NULL);
 
             for (i = 0; i < n; i++) {
                 if (pattern & (1ul << i)) {
@@ -308,6 +349,7 @@ test_hmap_for_each_pop(hash_func *hash)
             i++;
         }
         assert(i == n);
+        assert(e == NULL);
 
         hmap_destroy(&hmap);
     }
diff --git a/tests/test-list.c b/tests/test-list.c
index 6f1fb059bc..2c6c444488 100644
--- a/tests/test-list.c
+++ b/tests/test-list.c
@@ -61,7 +61,7 @@ check_list(struct ovs_list *list, const int values[], size_t n)
         assert(e->value == values[i]);
         i++;
     }
-    assert(&e->node == list);
+    assert(e == NULL);
     assert(i == n);
 
     i = 0;
@@ -70,7 +70,7 @@ check_list(struct ovs_list *list, const int values[], size_t n)
         assert(e->value == values[n - i - 1]);
         i++;
     }
-    assert(&e->node == list);
+    assert(e == NULL);
     assert(i == n);
 
     assert(ovs_list_is_empty(list) == !n);
@@ -135,6 +135,13 @@ test_list_for_each_safe(void)
             values_idx = 0;
             n_remaining = n;
             LIST_FOR_EACH_SAFE (e, next, node, &list) {
+                /* "next" is valid as long as it's not pointing to &list. */
+                if (&e->node == list.prev) {
+                    assert(next == NULL);
+                } else {
+                    assert(&next->node == e->node.next);
+                }
+
                 assert(i < n);
                 if (pattern & (1ul << i)) {
                     ovs_list_remove(&e->node);
@@ -148,7 +155,8 @@ test_list_for_each_safe(void)
                 i++;
             }
             assert(i == n);
-            assert(&e->node == &list);
+            assert(e == NULL);
+            assert(next == NULL);
 
             for (i = 0; i < n; i++) {
                 if (pattern & (1ul << i)) {
@@ -156,6 +164,35 @@ test_list_for_each_safe(void)
                 }
             }
             assert(n == n_remaining);
+
+            /* Test short version (without next variable). */
+            make_list(&list, elements, values, n);
+
+            i = 0;
+            values_idx = 0;
+            n_remaining = n;
+            LIST_FOR_EACH_SAFE (e, node, &list) {
+                assert(i < n);
+                if (pattern & (1ul << i)) {
+                    ovs_list_remove(&e->node);
+                    n_remaining--;
+                    memmove(&values[values_idx], &values[values_idx + 1],
+                            sizeof *values * (n_remaining - values_idx));
+                } else {
+                    values_idx++;
+                }
+
+                check_list(&list, values, n_remaining);
+                i++;
+            }
+            assert(i == n);
+            assert(e == NULL);
+
+            for (i = 0; i < n; i++) {
+                if (pattern & (1ul << i)) {
+                    n_remaining++;
+                }
+            }
         }
     }
 }
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index 57589758f4..c63344196b 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -546,6 +546,28 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
   port  7: rx pkts=5, bytes=434, drop=?, errs=?, frame=?, over=?, crc=?
 ])
 
+dnl Send out packets received from L3GRE tunnel back to L3GRE tunnel
+AT_CHECK([ovs-ofctl del-flows int-br])
+AT_CHECK([ovs-ofctl add-flow int-br "in_port=7,actions=set_field:3->in_port,7"])
+AT_CHECK([ovs-vsctl -- set Interface br0 options:pcap=br0.pcap])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-pcap p0.pcap > p0.pcap.txt 2>&1])
+AT_CHECK([tail -6 p0.pcap.txt], [0], [dnl
+aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
+001b213cab64aa55aa55000008004500007000004000402f33aa010102580101025c20000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
+aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
+001b213cab64aa55aa55000008004500007000004000402f33aa010102580101025c20000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
+aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
+001b213cab64aa55aa55000008004500007000004000402f33aa010102580101025c20000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
+])
+
+
 dnl Check decapsulation of Geneve packet with options
 AT_CAPTURE_FILE([ofctl_monitor.log])
 AT_CHECK([ovs-ofctl monitor int-br 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
@@ -565,8 +587,8 @@ icmp,vlan_tci=0x0000,dl_src=be:b6:f4:e1:49:4a,dl_dst=fe:71:d8:83:72:4f,nw_src=30
 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], [0], [dnl
   port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
 ])
-AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], [0], [dnl
-tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
+AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)' | sed -e 's/recirc_id=[[0-9]]*/recirc_id=<cleared>/g'], [0], [dnl
+tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=<cleared>,rule_cookie=0,controller_id=0,max_len=65535))
 ])
 
 dnl Receive VXLAN with different MAC and verify that the neigh cache gets updated
@@ -842,3 +864,54 @@ Datapath actions: 7
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([tunnel_push_pop - VXLAN access port])
+
+dnl Create bridge that has a MAC address.
+OVS_VSWITCHD_START([set bridge br0 datapath_type=dummy dnl
+                    -- set Interface br0 other-config:hwaddr=aa:55:aa:55:00:00])
+AT_CHECK([ovs-vsctl add-port br0 p8 dnl
+                    -- set Interface p8 type=dummy ofport_request=8])
+
+dnl Create another bridge.
+AT_CHECK([ovs-vsctl add-br ovs-tun0 -- set bridge ovs-tun0 datapath_type=dummy])
+
+dnl Add VXLAN port to this bridge.
+AT_CHECK([ovs-vsctl add-port ovs-tun0 tun0 dnl
+            -- set int tun0 type=vxlan options:remote_ip=10.0.0.11 dnl
+            -- add-port ovs-tun0 p7 dnl
+            -- set interface p7 type=dummy ofport_request=7])
+
+dnl Set VLAN tags, so that br0 and its port p8 have the same tag,
+dnl but ovs-tun0's port p7 has a different tag.
+AT_CHECK([ovs-vsctl set port p8  tag=42 dnl
+                 -- set port br0 tag=42 dnl
+                 -- set port p7  tag=200])
+
+dnl Set IP address and route for br0.
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 10.0.0.2/24], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 10.0.0.11/24 br0], [0], [OK
+])
+
+dnl Send an ARP reply to port b8 on br0, so that packets will be forwarded
+dnl to learned port.
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),dnl
+   eth(src=aa:55:aa:66:00:00,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl
+   arp(sip=10.0.0.11,tip=10.0.0.2,op=2,sha=aa:55:aa:66:00:00,tha=00:00:00:00:00:00)'])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-tun0 in_port=p7], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0], [dnl
+Megaflow: recirc_id=0,eth,in_port=7,dl_src=00:00:00:00:00:00,dnl
+dl_dst=00:00:00:00:00:00,dl_type=0x0000
+Datapath actions: push_vlan(vid=200,pcp=0),1,clone(tnl_push(tnl_port(4789),dnl
+header(size=50,type=4,eth(dst=aa:55:aa:66:00:00,src=aa:55:aa:55:00:00,dnl
+dl_type=0x0800),ipv4(src=10.0.0.2,dst=10.0.0.11,proto=17,tos=0,ttl=64,dnl
+frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),dnl
+out_port(100)),8)
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/tests/tunnel.at b/tests/tunnel.at
index b8ae7caa9b..fd482aa872 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -126,7 +126,7 @@ AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
 AT_CHECK([ovs-appctl dpctl/add-flow "tunnel(dst=1.1.1.1,src=3.3.3.200/255.255.255.0,tp_dst=123,tp_src=1,ttl=64),recirc_id(0),in_port(1),eth(),eth_type(0x0800),ipv4()" "2"])
 
 AT_CHECK([ovs-appctl dpctl/dump-flows | tail -1], [0], [dnl
-tunnel(src=3.3.3.200/255.255.255.0,dst=1.1.1.1,ttl=64,tp_src=1,tp_dst=123),recirc_id(0),in_port(1),eth_type(0x0800), packets:0, bytes:0, used:never, actions:2
+tunnel(src=3.3.3.200/255.255.255.0,dst=1.1.1.1,ttl=64,tp_src=1,tp_dst=123),recirc_id(0),in_port(1),eth(),eth_type(0x0800), packets:0, bytes:0, used:never, actions:2
 ])
 
 OVS_VSWITCHD_STOP
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index ede7f1e61a..6771973ae9 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -730,12 +730,12 @@ static void
 bundle_print_errors(struct ovs_list *errors, struct ovs_list *requests,
                     const char *vconn_name)
 {
-    struct ofpbuf *error, *next;
+    struct ofpbuf *error;
     struct ofpbuf *bmsg;
 
     INIT_CONTAINER(bmsg, requests, list_node);
 
-    LIST_FOR_EACH_SAFE (error, next, list_node, errors) {
+    LIST_FOR_EACH_SAFE (error, list_node, errors) {
         const struct ofp_header *error_oh = error->data;
         ovs_be32 error_xid = error_oh->xid;
         enum ofperr ofperr;
diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
index 37cc72d401..1032089fc2 100644
--- a/utilities/ovs-vsctl.c
+++ b/utilities/ovs-vsctl.c
@@ -1100,14 +1100,14 @@ cmd_emer_reset(struct ctl_context *ctx)
     const struct ovsrec_bridge *br;
     const struct ovsrec_port *port;
     const struct ovsrec_interface *iface;
-    const struct ovsrec_mirror *mirror, *next_mirror;
-    const struct ovsrec_controller *ctrl, *next_ctrl;
-    const struct ovsrec_manager *mgr, *next_mgr;
-    const struct ovsrec_netflow *nf, *next_nf;
-    const struct ovsrec_ssl *ssl, *next_ssl;
-    const struct ovsrec_sflow *sflow, *next_sflow;
-    const struct ovsrec_ipfix *ipfix, *next_ipfix;
-    const struct ovsrec_flow_sample_collector_set *fscset, *next_fscset;
+    const struct ovsrec_mirror *mirror;
+    const struct ovsrec_controller *ctrl;
+    const struct ovsrec_manager *mgr;
+    const struct ovsrec_netflow *nf;
+    const struct ovsrec_ssl *ssl;
+    const struct ovsrec_sflow *sflow;
+    const struct ovsrec_ipfix *ipfix;
+    const struct ovsrec_flow_sample_collector_set *fscset;
 
     /* Reset the Open_vSwitch table. */
     ovsrec_open_vswitch_set_manager_options(vsctl_ctx->ovs, NULL, 0);
@@ -1145,35 +1145,35 @@ cmd_emer_reset(struct ctl_context *ctx)
         ovsrec_interface_set_ingress_policing_burst(iface, 0);
     }
 
-    OVSREC_MIRROR_FOR_EACH_SAFE (mirror, next_mirror, idl) {
+    OVSREC_MIRROR_FOR_EACH_SAFE (mirror, idl) {
         ovsrec_mirror_delete(mirror);
     }
 
-    OVSREC_CONTROLLER_FOR_EACH_SAFE (ctrl, next_ctrl, idl) {
+    OVSREC_CONTROLLER_FOR_EACH_SAFE (ctrl, idl) {
         ovsrec_controller_delete(ctrl);
     }
 
-    OVSREC_MANAGER_FOR_EACH_SAFE (mgr, next_mgr, idl) {
+    OVSREC_MANAGER_FOR_EACH_SAFE (mgr, idl) {
         ovsrec_manager_delete(mgr);
     }
 
-    OVSREC_NETFLOW_FOR_EACH_SAFE (nf, next_nf, idl) {
+    OVSREC_NETFLOW_FOR_EACH_SAFE (nf, idl) {
         ovsrec_netflow_delete(nf);
     }
 
-    OVSREC_SSL_FOR_EACH_SAFE (ssl, next_ssl, idl) {
+    OVSREC_SSL_FOR_EACH_SAFE (ssl, idl) {
         ovsrec_ssl_delete(ssl);
     }
 
-    OVSREC_SFLOW_FOR_EACH_SAFE (sflow, next_sflow, idl) {
+    OVSREC_SFLOW_FOR_EACH_SAFE (sflow, idl) {
         ovsrec_sflow_delete(sflow);
     }
 
-    OVSREC_IPFIX_FOR_EACH_SAFE (ipfix, next_ipfix, idl) {
+    OVSREC_IPFIX_FOR_EACH_SAFE (ipfix, idl) {
         ovsrec_ipfix_delete(ipfix);
     }
 
-    OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset, next_fscset, idl) {
+    OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset, idl) {
         ovsrec_flow_sample_collector_set_delete(fscset);
     }
 
@@ -1510,13 +1510,13 @@ cmd_add_br(struct ctl_context *ctx)
 static void
 del_port(struct vsctl_context *vsctl_ctx, struct vsctl_port *port)
 {
-    struct vsctl_iface *iface, *next_iface;
+    struct vsctl_iface *iface;
 
     bridge_delete_port((port->bridge->parent
                         ? port->bridge->parent->br_cfg
                         : port->bridge->br_cfg), port->port_cfg);
 
-    LIST_FOR_EACH_SAFE (iface, next_iface, ifaces_node, &port->ifaces) {
+    LIST_FOR_EACH_SAFE (iface, ifaces_node, &port->ifaces) {
         del_cached_iface(vsctl_ctx, iface);
     }
     del_cached_port(vsctl_ctx, port);
@@ -1525,19 +1525,19 @@ del_port(struct vsctl_context *vsctl_ctx, struct vsctl_port *port)
 static void
 del_bridge(struct vsctl_context *vsctl_ctx, struct vsctl_bridge *br)
 {
-    struct vsctl_bridge *child, *next_child;
-    struct vsctl_port *port, *next_port;
-    const struct ovsrec_flow_sample_collector_set *fscset, *next_fscset;
+    struct vsctl_bridge *child;
+    struct vsctl_port *port;
+    const struct ovsrec_flow_sample_collector_set *fscset;
 
-    HMAP_FOR_EACH_SAFE (child, next_child, children_node, &br->children) {
+    HMAP_FOR_EACH_SAFE (child, children_node, &br->children) {
         del_bridge(vsctl_ctx, child);
     }
 
-    LIST_FOR_EACH_SAFE (port, next_port, ports_node, &br->ports) {
+    LIST_FOR_EACH_SAFE (port, ports_node, &br->ports) {
         del_port(vsctl_ctx, port);
     }
 
-    OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset, next_fscset,
+    OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset,
                                                     vsctl_ctx->base.idl) {
         if (fscset->bridge == br->br_cfg) {
             ovsrec_flow_sample_collector_set_delete(fscset);
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 5223aa8970..e328d8ead1 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -543,13 +543,13 @@ bridge_exit(bool delete_datapath)
     if_notifier_destroy(ifnotifier);
     seq_destroy(ifaces_changed);
 
-    struct datapath *dp, *next;
-    HMAP_FOR_EACH_SAFE (dp, next, node, &all_datapaths) {
+    struct datapath *dp;
+    HMAP_FOR_EACH_SAFE (dp, node, &all_datapaths) {
         datapath_destroy(dp);
     }
 
-    struct bridge *br, *next_br;
-    HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
+    struct bridge *br;
+    HMAP_FOR_EACH_SAFE (br, node, &all_bridges) {
         bridge_destroy(br, delete_datapath);
     }
 
@@ -716,8 +716,8 @@ static void
 datapath_destroy(struct datapath *dp)
 {
     if (dp) {
-        struct ct_zone *ct_zone, *next;
-        HMAP_FOR_EACH_SAFE (ct_zone, next, node, &dp->ct_zones) {
+        struct ct_zone *ct_zone;
+        HMAP_FOR_EACH_SAFE (ct_zone, node, &dp->ct_zones) {
             ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
             ct_zone_remove_and_destroy(dp, ct_zone);
         }
@@ -733,7 +733,7 @@ datapath_destroy(struct datapath *dp)
 static void
 ct_zones_reconfigure(struct datapath *dp, struct ovsrec_datapath *dp_cfg)
 {
-    struct ct_zone *ct_zone, *next;
+    struct ct_zone *ct_zone;
 
     /* Add new 'ct_zone's or update existing 'ct_zone's based on the database
      * state. */
@@ -760,7 +760,7 @@ ct_zones_reconfigure(struct datapath *dp, struct ovsrec_datapath *dp_cfg)
     }
 
     /* Purge 'ct_zone's no longer found in the database. */
-    HMAP_FOR_EACH_SAFE (ct_zone, next, node, &dp->ct_zones) {
+    HMAP_FOR_EACH_SAFE (ct_zone, node, &dp->ct_zones) {
         if (ct_zone->last_used != idl_seqno) {
             ofproto_ct_del_zone_timeout_policy(dp->type, ct_zone->zone_id);
             ct_zone_remove_and_destroy(dp, ct_zone);
@@ -788,7 +788,7 @@ dp_capability_reconfigure(struct datapath *dp,
 static void
 datapath_reconfigure(const struct ovsrec_open_vswitch *cfg)
 {
-    struct datapath *dp, *next;
+    struct datapath *dp;
 
     /* Add new 'datapath's or update existing ones. */
     for (size_t i = 0; i < cfg->n_datapaths; i++) {
@@ -805,7 +805,7 @@ datapath_reconfigure(const struct ovsrec_open_vswitch *cfg)
     }
 
     /* Purge deleted 'datapath's. */
-    HMAP_FOR_EACH_SAFE (dp, next, node, &all_datapaths) {
+    HMAP_FOR_EACH_SAFE (dp, node, &all_datapaths) {
         if (dp->last_used != idl_seqno) {
             datapath_destroy(dp);
         }
@@ -816,7 +816,7 @@ static void
 bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
 {
     struct sockaddr_in *managers;
-    struct bridge *br, *next;
+    struct bridge *br;
     int sflow_bridge_number;
     size_t n_managers;
 
@@ -875,7 +875,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
      *     - Create ofprotos that are missing.
      *
      *     - Add ports that are missing. */
-    HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
+    HMAP_FOR_EACH_SAFE (br, node, &all_bridges) {
         if (!br->ofproto) {
             int error;
 
@@ -1020,7 +1020,7 @@ bridge_delete_or_reconfigure_ports(struct bridge *br)
     struct ofproto_port_dump dump;
 
     struct sset ofproto_ports;
-    struct port *port, *port_next;
+    struct port *port;
 
     /* List of "ofp_port"s to delete.  We make a list instead of deleting them
      * right away because ofproto implementations aren't necessarily able to
@@ -1132,10 +1132,10 @@ bridge_delete_or_reconfigure_ports(struct bridge *br)
      *       device destroyed via "tunctl -d", a physical Ethernet device
      *       whose module was just unloaded via "rmmod", or a virtual NIC for a
      *       VM whose VM was just terminated. */
-    HMAP_FOR_EACH_SAFE (port, port_next, hmap_node, &br->ports) {
-        struct iface *iface, *iface_next;
+    HMAP_FOR_EACH_SAFE (port, hmap_node, &br->ports) {
+        struct iface *iface;
 
-        LIST_FOR_EACH_SAFE (iface, iface_next, port_elem, &port->ifaces) {
+        LIST_FOR_EACH_SAFE (iface, port_elem, &port->ifaces) {
             if (!sset_contains(&ofproto_ports, iface->name)) {
                 iface_destroy__(iface);
             }
@@ -1967,7 +1967,7 @@ port_is_bond_fake_iface(const struct port *port)
 static void
 add_del_bridges(const struct ovsrec_open_vswitch *cfg)
 {
-    struct bridge *br, *next;
+    struct bridge *br;
     struct shash_node *node;
     struct shash new_br;
     size_t i;
@@ -1993,7 +1993,7 @@ add_del_bridges(const struct ovsrec_open_vswitch *cfg)
 
     /* Get rid of deleted bridges or those whose types have changed.
      * Update 'cfg' of bridges that still exist. */
-    HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) {
+    HMAP_FOR_EACH_SAFE (br, node, &all_bridges) {
         br->cfg = shash_find_data(&new_br, br->name);
         if (!br->cfg || strcmp(br->type, ofproto_normalize_type(
                                    br->cfg->datapath_type))) {
@@ -3266,13 +3266,13 @@ bridge_run(void)
 
     if (ovsdb_idl_is_lock_contended(idl)) {
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-        struct bridge *br, *next_br;
+        struct bridge *br;
 
         VLOG_ERR_RL(&rl, "another ovs-vswitchd process is running, "
                     "disabling this process (pid %ld) until it goes away",
                     (long int) getpid());
 
-        HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) {
+        HMAP_FOR_EACH_SAFE (br, node, &all_bridges) {
             bridge_destroy(br, false);
         }
         /* Since we will not be running system_stats_run() in this process
@@ -3594,13 +3594,13 @@ static void
 bridge_destroy(struct bridge *br, bool del)
 {
     if (br) {
-        struct mirror *mirror, *next_mirror;
-        struct port *port, *next_port;
+        struct mirror *mirror;
+        struct port *port;
 
-        HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) {
+        HMAP_FOR_EACH_SAFE (port, hmap_node, &br->ports) {
             port_destroy(port);
         }
-        HMAP_FOR_EACH_SAFE (mirror, next_mirror, hmap_node, &br->mirrors) {
+        HMAP_FOR_EACH_SAFE (mirror, hmap_node, &br->mirrors) {
             mirror_destroy(mirror);
         }
 
@@ -3746,11 +3746,11 @@ static void
 bridge_del_ports(struct bridge *br, const struct shash *wanted_ports)
 {
     struct shash_node *port_node;
-    struct port *port, *next;
+    struct port *port;
 
     /* Get rid of deleted ports.
      * Get rid of deleted interfaces on ports that still exist. */
-    HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) {
+    HMAP_FOR_EACH_SAFE (port, hmap_node, &br->ports) {
         port->cfg = shash_find_data(wanted_ports, port->name);
         if (!port->cfg) {
             port_destroy(port);
@@ -4211,7 +4211,7 @@ bridge_configure_aa(struct bridge *br)
     const struct ovsdb_datum *mc;
     struct ovsrec_autoattach *auto_attach = br->cfg->auto_attach;
     struct aa_settings aa_s;
-    struct aa_mapping *m, *next;
+    struct aa_mapping *m;
     size_t i;
 
     if (!auto_attach) {
@@ -4227,7 +4227,7 @@ bridge_configure_aa(struct bridge *br)
     mc = ovsrec_autoattach_get_mappings(auto_attach,
                                         OVSDB_TYPE_INTEGER,
                                         OVSDB_TYPE_INTEGER);
-    HMAP_FOR_EACH_SAFE (m, next, hmap_node, &br->mappings) {
+    HMAP_FOR_EACH_SAFE (m, hmap_node, &br->mappings) {
         union ovsdb_atom atom;
 
         atom.integer = m->isid;
@@ -4341,12 +4341,12 @@ static void
 bridge_aa_refresh_queued(struct bridge *br)
 {
     struct ovs_list *list = xmalloc(sizeof *list);
-    struct bridge_aa_vlan *node, *next;
+    struct bridge_aa_vlan *node;
 
     ovs_list_init(list);
     ofproto_aa_vlan_get_queued(br->ofproto, list);
 
-    LIST_FOR_EACH_SAFE (node, next, list_node, list) {
+    LIST_FOR_EACH_SAFE (node, list_node, list) {
         struct port *port;
 
         VLOG_INFO("ifname=%s, vlan=%u, oper=%u", node->port_name, node->vlan,
@@ -4387,7 +4387,7 @@ port_create(struct bridge *br, const struct ovsrec_port *cfg)
 static void
 port_del_ifaces(struct port *port)
 {
-    struct iface *iface, *next;
+    struct iface *iface;
     struct sset new_ifaces;
     size_t i;
 
@@ -4398,7 +4398,7 @@ port_del_ifaces(struct port *port)
     }
 
     /* Get rid of deleted interfaces. */
-    LIST_FOR_EACH_SAFE (iface, next, port_elem, &port->ifaces) {
+    LIST_FOR_EACH_SAFE (iface, port_elem, &port->ifaces) {
         if (!sset_contains(&new_ifaces, iface->name)) {
             iface_destroy(iface);
         }
@@ -4412,13 +4412,13 @@ port_destroy(struct port *port)
 {
     if (port) {
         struct bridge *br = port->bridge;
-        struct iface *iface, *next;
+        struct iface *iface;
 
         if (br->ofproto) {
             ofproto_bundle_unregister(br->ofproto, port);
         }
 
-        LIST_FOR_EACH_SAFE (iface, next, port_elem, &port->ifaces) {
+        LIST_FOR_EACH_SAFE (iface, port_elem, &port->ifaces) {
             iface_destroy__(iface);
         }
 
@@ -5013,12 +5013,12 @@ bridge_configure_mirrors(struct bridge *br)
 {
     const struct ovsdb_datum *mc;
     unsigned long *flood_vlans;
-    struct mirror *m, *next;
+    struct mirror *m;
     size_t i;
 
     /* Get rid of deleted mirrors. */
     mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID);
-    HMAP_FOR_EACH_SAFE (m, next, hmap_node, &br->mirrors) {
+    HMAP_FOR_EACH_SAFE (m, hmap_node, &br->mirrors) {
         union ovsdb_atom atom;
 
         atom.uuid = m->uuid;
diff --git a/vtep/vtep-ctl.c b/vtep/vtep-ctl.c
index ab552457d9..99c4adcd53 100644
--- a/vtep/vtep-ctl.c
+++ b/vtep/vtep-ctl.c
@@ -801,16 +801,16 @@ vtep_ctl_context_invalidate_cache(struct ctl_context *ctx)
 
     SHASH_FOR_EACH (node, &vtepctl_ctx->lswitches) {
         struct vtep_ctl_lswitch *ls = node->data;
-        struct shash_node *node2, *next_node2;
+        struct shash_node *node2;
 
         shash_destroy(&ls->ucast_local);
         shash_destroy(&ls->ucast_remote);
 
-        SHASH_FOR_EACH_SAFE (node2, next_node2, &ls->mcast_local) {
+        SHASH_FOR_EACH_SAFE (node2, &ls->mcast_local) {
             struct vtep_ctl_mcast_mac *mcast_mac = node2->data;
-            struct vtep_ctl_ploc *ploc, *next_ploc;
+            struct vtep_ctl_ploc *ploc;
 
-            LIST_FOR_EACH_SAFE (ploc, next_ploc, locators_node,
+            LIST_FOR_EACH_SAFE (ploc, locators_node,
                                 &mcast_mac->locators) {
                 free(ploc);
             }
@@ -818,11 +818,11 @@ vtep_ctl_context_invalidate_cache(struct ctl_context *ctx)
         }
         shash_destroy(&ls->mcast_local);
 
-        SHASH_FOR_EACH_SAFE (node2, next_node2, &ls->mcast_remote) {
+        SHASH_FOR_EACH_SAFE (node2, &ls->mcast_remote) {
             struct vtep_ctl_mcast_mac *mcast_mac = node2->data;
-            struct vtep_ctl_ploc *ploc, *next_ploc;
+            struct vtep_ctl_ploc *ploc;
 
-            LIST_FOR_EACH_SAFE (ploc, next_ploc, locators_node,
+            LIST_FOR_EACH_SAFE (ploc, locators_node,
                                 &mcast_mac->locators) {
                 free(ploc);
             }
@@ -1229,9 +1229,9 @@ del_port(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_port *port)
 static void
 del_pswitch(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_pswitch *ps)
 {
-    struct vtep_ctl_port *port, *next_port;
+    struct vtep_ctl_port *port;
 
-    LIST_FOR_EACH_SAFE (port, next_port, ports_node, &ps->ports) {
+    LIST_FOR_EACH_SAFE (port, ports_node, &ps->ports) {
         del_port(vtepctl_ctx, port);
     }