diff --git a/SOURCES/openvswitch-2.16.0.patch b/SOURCES/openvswitch-2.16.0.patch
index c82ee65..1241304 100644
--- a/SOURCES/openvswitch-2.16.0.patch
+++ b/SOURCES/openvswitch-2.16.0.patch
@@ -3035,7 +3035,7 @@ index 9845e8d3fe..e1568e38a0 100644
      }
  
 diff --git a/lib/odp-util.c b/lib/odp-util.c
-index 7729a90608..20d663153b 100644
+index 7729a90608..36e7161714 100644
 --- a/lib/odp-util.c
 +++ b/lib/odp-util.c
 @@ -2941,7 +2941,7 @@ odp_nsh_key_from_attr__(const struct nlattr *attr, bool is_mask,
@@ -3077,6 +3077,18 @@ index 7729a90608..20d663153b 100644
              const struct nlattr *ma = nl_attr_find__(mask, mask_len,
                                                       OVS_KEY_ATTR_ETHERTYPE);
              if (ma) {
+@@ -7132,11 +7137,6 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
+                 }
+             }
+         }
+-    } else if (src_flow->nw_proto == IPPROTO_IGMP
+-               && src_flow->dl_type == htons(ETH_TYPE_IP)) {
+-        /* OVS userspace parses the IGMP type, code, and group, but its
+-         * datapaths do not, so there is always missing information. */
+-        return ODP_FIT_TOO_LITTLE;
+     }
+     if (is_mask && expected_bit != OVS_KEY_ATTR_UNSPEC) {
+         if ((flow->tp_src || flow->tp_dst) && flow->nw_proto != 0xff) {
 diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
 index ecf914eac1..7ea4b6ed56 100644
 --- a/lib/ofp-actions.c
@@ -5140,7 +5152,7 @@ index 114aff8ea3..0fc6d2ea60 100644
      enum xc_type type;
      union {
 diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
-index a426fcfeb6..4c16aa3f99 100644
+index a426fcfeb6..df44c18e7d 100644
 --- a/ofproto/ofproto-dpif-xlate.c
 +++ b/ofproto/ofproto-dpif-xlate.c
 @@ -460,7 +460,7 @@ static void xlate_commit_actions(struct xlate_ctx *ctx);
@@ -5242,7 +5254,15 @@ index a426fcfeb6..4c16aa3f99 100644
      }
  
      /* Determine output bundle. */
-@@ -3272,7 +3296,9 @@ compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t output_odp_port)
+@@ -3048,7 +3072,6 @@ xlate_normal(struct xlate_ctx *ctx)
+              */
+             ctx->xout->slow |= SLOW_ACTION;
+ 
+-            memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+             if (mcast_snooping_is_membership(flow->tp_src) ||
+                 mcast_snooping_is_query(flow->tp_src)) {
+                 if (ctx->xin->allow_side_effects && ctx->xin->packet) {
+@@ -3272,7 +3295,9 @@ compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t output_odp_port)
      struct dpif_ipfix *ipfix = ctx->xbridge->ipfix;
      odp_port_t tunnel_out_port = ODPP_NONE;
  
@@ -5253,7 +5273,7 @@ index a426fcfeb6..4c16aa3f99 100644
          return;
      }
  
-@@ -3521,6 +3547,9 @@ propagate_tunnel_data_to_flow__(struct flow *dst_flow,
+@@ -3521,6 +3546,9 @@ propagate_tunnel_data_to_flow__(struct flow *dst_flow,
      dst_flow->dl_dst = dmac;
      dst_flow->dl_src = smac;
  
@@ -5263,7 +5283,7 @@ index a426fcfeb6..4c16aa3f99 100644
      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;
-@@ -3598,7 +3627,7 @@ propagate_tunnel_data_to_flow(struct xlate_ctx *ctx, struct eth_addr dmac,
+@@ -3598,7 +3626,7 @@ propagate_tunnel_data_to_flow(struct xlate_ctx *ctx, struct eth_addr dmac,
  static int
  native_tunnel_output(struct xlate_ctx *ctx, const struct xport *xport,
                       const struct flow *flow, odp_port_t tunnel_odp_port,
@@ -5272,7 +5292,7 @@ index a426fcfeb6..4c16aa3f99 100644
  {
      struct netdev_tnl_build_header_params tnl_params;
      struct ovs_action_push_tnl tnl_push_data;
-@@ -3728,7 +3757,7 @@ native_tunnel_output(struct xlate_ctx *ctx, const struct xport *xport,
+@@ -3728,7 +3756,7 @@ native_tunnel_output(struct xlate_ctx *ctx, const struct xport *xport,
          entry->tunnel_hdr.hdr_size = tnl_push_data.header_len;
          entry->tunnel_hdr.operation = ADD;
  
@@ -5281,7 +5301,7 @@ index a426fcfeb6..4c16aa3f99 100644
  
          /* Similar to the stats update in revalidation, the x_cache entries
           * are populated by the previous translation are used to update the
-@@ -3822,7 +3851,7 @@ xlate_flow_is_protected(const struct xlate_ctx *ctx, const struct flow *flow, co
+@@ -3822,7 +3850,7 @@ xlate_flow_is_protected(const struct xlate_ctx *ctx, const struct flow *flow, co
   */
  static void
  patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev,
@@ -5290,7 +5310,7 @@ index a426fcfeb6..4c16aa3f99 100644
  {
      struct flow *flow = &ctx->xin->flow;
      struct flow old_flow = ctx->xin->flow;
-@@ -3864,8 +3893,9 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev,
+@@ -3864,8 +3892,9 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev,
      if (!process_special(ctx, out_dev) && may_receive(out_dev, ctx)) {
          if (xport_stp_forward_state(out_dev) &&
              xport_rstp_forward_state(out_dev)) {
@@ -5301,7 +5321,7 @@ index a426fcfeb6..4c16aa3f99 100644
              if (!ctx->freezing) {
                  xlate_action_set(ctx);
              }
-@@ -3880,7 +3910,7 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev,
+@@ -3880,7 +3909,7 @@ patch_port_output(struct xlate_ctx *ctx, const struct xport *in_dev,
              mirror_mask_t old_mirrors2 = ctx->mirrors;
  
              xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true,
@@ -5310,7 +5330,7 @@ index a426fcfeb6..4c16aa3f99 100644
              ctx->mirrors = old_mirrors2;
              ctx->base_flow = old_base_flow;
              ctx->odp_actions->size = old_size;
-@@ -4097,7 +4127,21 @@ terminate_native_tunnel(struct xlate_ctx *ctx, struct flow *flow,
+@@ -4097,7 +4126,21 @@ terminate_native_tunnel(struct xlate_ctx *ctx, struct flow *flow,
              (flow->dl_type == htons(ETH_TYPE_ARP) ||
               flow->nw_proto == IPPROTO_ICMPV6) &&
               is_neighbor_reply_correct(ctx, flow)) {
@@ -5333,7 +5353,7 @@ index a426fcfeb6..4c16aa3f99 100644
          }
      }
  
-@@ -4107,7 +4151,7 @@ terminate_native_tunnel(struct xlate_ctx *ctx, struct flow *flow,
+@@ -4107,7 +4150,7 @@ terminate_native_tunnel(struct xlate_ctx *ctx, struct flow *flow,
  static void
  compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
                          const struct xlate_bond_recirc *xr, bool check_stp,
@@ -5342,7 +5362,7 @@ index a426fcfeb6..4c16aa3f99 100644
  {
      const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
      struct flow_wildcards *wc = ctx->wc;
-@@ -4137,6 +4181,10 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+@@ -4137,6 +4180,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));
@@ -5353,7 +5373,7 @@ index a426fcfeb6..4c16aa3f99 100644
          }
      }
  
-@@ -4144,7 +4192,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+@@ -4144,7 +4191,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
         if (truncate) {
             xlate_report_error(ctx, "Cannot truncate output to patch port");
         }
@@ -5362,7 +5382,7 @@ index a426fcfeb6..4c16aa3f99 100644
         return;
      }
  
-@@ -4239,7 +4287,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+@@ -4239,7 +4286,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
                             xr->recirc_id);
          } else if (is_native_tunnel) {
              /* Output to native tunnel port. */
@@ -5372,7 +5392,7 @@ index a426fcfeb6..4c16aa3f99 100644
              flow->tunnel = flow_tnl; /* Restore tunnel metadata */
  
          } else if (terminate_native_tunnel(ctx, flow, wc,
-@@ -6177,11 +6226,32 @@ static void
+@@ -6177,11 +6225,32 @@ static void
  compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
                           bool is_last_action)
  {
@@ -5408,7 +5428,7 @@ index a426fcfeb6..4c16aa3f99 100644
      /* Ensure that any prior actions are applied before composing the new
       * conntrack action. */
      xlate_commit_actions(ctx);
-@@ -6193,11 +6263,6 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
+@@ -6193,11 +6262,6 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc,
      do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx,
                       is_last_action, false);
  
@@ -5420,7 +5440,7 @@ index a426fcfeb6..4c16aa3f99 100644
  
      ct_offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_CT);
      if (ofc->flags & NX_CT_F_COMMIT) {
-@@ -6333,6 +6398,7 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+@@ -6333,6 +6397,7 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
       * then ctx->exit would be true. Reset to false so that we can
       * do flow translation for 'IF_LESS_EQUAL' case. finish_freezing()
       * would have taken care of Undoing the changes done for freeze. */
@@ -5428,7 +5448,7 @@ index a426fcfeb6..4c16aa3f99 100644
      ctx->exit = false;
  
      offset_attr = nl_msg_start_nested(
-@@ -6357,7 +6423,7 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
+@@ -6357,7 +6422,7 @@ xlate_check_pkt_larger(struct xlate_ctx *ctx,
      ctx->was_mpls = old_was_mpls;
      ctx->conntracked = old_conntracked;
      ctx->xin->flow = old_flow;
@@ -5437,7 +5457,7 @@ index a426fcfeb6..4c16aa3f99 100644
  }
  
  static void
-@@ -6738,13 +6804,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -6738,13 +6803,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
          return;
      }
  
@@ -5453,7 +5473,7 @@ index a426fcfeb6..4c16aa3f99 100644
  
          if (ctx->error) {
              break;
-@@ -6752,7 +6819,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -6752,7 +6818,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
  
          recirc_for_mpls(a, ctx);
  
@@ -5462,7 +5482,7 @@ index a426fcfeb6..4c16aa3f99 100644
              /* Check if need to store the remaining actions for later
               * execution. */
              if (ctx->freezing) {
-@@ -7149,17 +7216,18 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
+@@ -7149,17 +7215,18 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
              break;
  
          case OFPACT_CHECK_PKT_LARGER: {
@@ -5499,7 +5519,7 @@ index 851088d794..2ba90e999c 100644
  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 cba49a99e1..f926d60142 100644
+index cba49a99e1..13f00e82d2 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);
@@ -5585,7 +5605,31 @@ index cba49a99e1..f926d60142 100644
          }
          if (rule) {
              goto out;   /* Match. */
-@@ -5779,15 +5780,7 @@ ofproto_dpif_lookup_by_name(const char *name)
+@@ -5556,6 +5557,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id,
+             ct_timeout_policy_unref(backer, ct_zone->ct_tp);
+             ct_zone->ct_tp = ct_tp;
+             ct_tp->ref_count++;
++            backer->need_revalidate = REV_RECONFIGURE;
+         }
+     } else {
+         struct ct_zone *new_ct_zone = ct_zone_alloc(zone_id);
+@@ -5563,6 +5565,7 @@ ct_set_zone_timeout_policy(const char *datapath_type, uint16_t zone_id,
+         cmap_insert(&backer->ct_zones, &new_ct_zone->node,
+                     hash_int(zone_id, 0));
+         ct_tp->ref_count++;
++        backer->need_revalidate = REV_RECONFIGURE;
+     }
+ }
+ 
+@@ -5579,6 +5582,7 @@ ct_del_zone_timeout_policy(const char *datapath_type, uint16_t zone_id)
+     if (ct_zone) {
+         ct_timeout_policy_unref(backer, ct_zone->ct_tp);
+         ct_zone_remove_and_destroy(backer, ct_zone);
++        backer->need_revalidate = REV_RECONFIGURE;
+     }
+ }
+ 
+@@ -5779,15 +5783,7 @@ ofproto_dpif_lookup_by_name(const char *name)
  struct ofproto_dpif *
  ofproto_dpif_lookup_by_uuid(const struct uuid *uuid)
  {
@@ -5602,7 +5646,7 @@ index cba49a99e1..f926d60142 100644
  }
  
  static void
-@@ -6496,6 +6489,7 @@ ofproto_unixctl_dpif_show_dp_features(struct unixctl_conn *conn,
+@@ -6496,6 +6492,7 @@ ofproto_unixctl_dpif_show_dp_features(struct unixctl_conn *conn,
  
      dpif_show_support(&ofproto->backer->bt_support, &ds);
      unixctl_command_reply(conn, ds_cstr(&ds));
@@ -6745,10 +6789,10 @@ index a69e37e5c2..48c6df511f 100644
  /* On disk data serialization and deserialization. */
  
 diff --git a/ovsdb/raft.c b/ovsdb/raft.c
-index 2fb5156519..855404808c 100644
+index 2fb5156519..b70fbed5d4 100644
 --- a/ovsdb/raft.c
 +++ b/ovsdb/raft.c
-@@ -74,6 +74,7 @@ enum raft_failure_test {
+@@ -74,9 +74,12 @@ enum raft_failure_test {
      FT_CRASH_BEFORE_SEND_EXEC_REQ,
      FT_CRASH_AFTER_SEND_EXEC_REQ,
      FT_CRASH_AFTER_RECV_APPEND_REQ_UPDATE,
@@ -6756,7 +6800,12 @@ index 2fb5156519..855404808c 100644
      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 *);
++    FT_TRANSFER_LEADERSHIP,
++    FT_TRANSFER_LEADERSHIP_AFTER_SEND_APPEND_REQ,
+ };
+ static enum raft_failure_test failure_test;
+ 
+@@ -379,12 +382,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);
@@ -6776,7 +6825,7 @@ index 2fb5156519..855404808c 100644
  static char *
  raft_make_address_passive(const char *address_)
  {
-@@ -494,11 +502,11 @@ raft_create_cluster(const char *file_name, const char *name,
+@@ -494,11 +504,11 @@ raft_create_cluster(const char *file_name, const char *name,
          .snap_index = index++,
          .snap = {
              .term = term,
@@ -6789,7 +6838,7 @@ index 2fb5156519..855404808c 100644
      shash_add_nocopy(json_object(h.snap.servers),
                       xasprintf(UUID_FMT, UUID_ARGS(&h.sid)),
                       json_string_create(local_address));
-@@ -727,10 +735,10 @@ raft_add_entry(struct raft *raft,
+@@ -727,10 +737,10 @@ raft_add_entry(struct raft *raft,
      uint64_t index = raft->log_end++;
      struct raft_entry *entry = &raft->entries[index - raft->log_start];
      entry->term = term;
@@ -6801,7 +6850,7 @@ index 2fb5156519..855404808c 100644
      return index;
  }
  
-@@ -741,13 +749,16 @@ raft_write_entry(struct raft *raft, uint64_t term, struct json *data,
+@@ -741,13 +751,16 @@ raft_write_entry(struct raft *raft, uint64_t term, struct json *data,
                   const struct uuid *eid, struct json *servers,
                   uint64_t election_timer)
  {
@@ -6821,7 +6870,7 @@ index 2fb5156519..855404808c 100644
              .servers = servers,
              .election_timer = election_timer,
              .eid = eid ? *eid : UUID_ZERO,
-@@ -1864,6 +1875,8 @@ raft_open_conn(struct raft *raft, const char *address, const struct uuid *sid)
+@@ -1864,6 +1877,8 @@ raft_open_conn(struct raft *raft, const char *address, const struct uuid *sid)
  static void
  raft_conn_close(struct raft_conn *conn)
  {
@@ -6830,7 +6879,21 @@ index 2fb5156519..855404808c 100644
      jsonrpc_session_close(conn->js);
      ovs_list_remove(&conn->list_node);
      free(conn->nickname);
-@@ -1954,16 +1967,30 @@ raft_run(struct raft *raft)
+@@ -1918,6 +1933,13 @@ raft_run(struct raft *raft)
+         return;
+     }
+ 
++    if (failure_test == FT_TRANSFER_LEADERSHIP) {
++        /* Using this function as it conveniently implements all we need and
++         * snapshotting is the main test scenario for leadership transfer. */
++        raft_notify_snapshot_recommended(raft);
++        failure_test = FT_NO_TEST;
++    }
++
+     raft_waiters_run(raft);
+ 
+     if (!raft->listener && time_msec() >= raft->listen_backoff) {
+@@ -1954,16 +1976,30 @@ raft_run(struct raft *raft)
      }
  
      /* Close unneeded sessions. */
@@ -6862,7 +6925,23 @@ index 2fb5156519..855404808c 100644
      HMAP_FOR_EACH (server, hmap_node, &raft->servers) {
          raft_open_conn(raft, server->address, &server->sid);
      }
-@@ -2161,7 +2188,7 @@ raft_get_eid(const struct raft *raft, uint64_t index)
+@@ -2040,7 +2076,14 @@ raft_run(struct raft *raft)
+         HMAP_FOR_EACH_SAFE (cmd, next_cmd, hmap_node, &raft->commands) {
+             if (cmd->timestamp
+                 && now - cmd->timestamp > raft->election_timer * 2) {
+-                raft_command_complete(raft, cmd, RAFT_CMD_TIMEOUT);
++                if (cmd->index && raft->role != RAFT_LEADER) {
++                    /* This server lost leadership and command didn't complete
++                     * in time.  Likely, it wasn't replicated to the majority
++                     * of servers before losing the leadership. */
++                    raft_command_complete(raft, cmd, RAFT_CMD_LOST_LEADERSHIP);
++                } else {
++                    raft_command_complete(raft, cmd, RAFT_CMD_TIMEOUT);
++                }
+             }
+         }
+         raft_reset_ping_timer(raft);
+@@ -2161,7 +2204,7 @@ raft_get_eid(const struct raft *raft, uint64_t index)
  {
      for (; index >= raft->log_start; index--) {
          const struct raft_entry *e = raft_get_entry(raft, index);
@@ -6871,7 +6950,41 @@ index 2fb5156519..855404808c 100644
              return &e->eid;
          }
      }
-@@ -2826,8 +2853,8 @@ raft_truncate(struct raft *raft, uint64_t new_end)
+@@ -2232,6 +2275,9 @@ raft_command_initiate(struct raft *raft,
+     if (failure_test == FT_CRASH_AFTER_SEND_APPEND_REQ) {
+         ovs_fatal(0, "Raft test: crash after sending append_request.");
+     }
++    if (failure_test == FT_TRANSFER_LEADERSHIP_AFTER_SEND_APPEND_REQ) {
++        failure_test = FT_TRANSFER_LEADERSHIP;
++    }
+     raft_reset_ping_timer(raft);
+ 
+     return cmd;
+@@ -2598,7 +2644,13 @@ raft_become_follower(struct raft *raft)
+      * configuration is already part of the log.  Possibly the configuration
+      * log entry will not be committed, but until we know that we must use the
+      * new configuration.  Our AppendEntries processing will properly update
+-     * the server configuration later, if necessary. */
++     * the server configuration later, if necessary.
++     *
++     * Also we do not complete commands here, as they can still be completed
++     * if their log entries have already been replicated to other servers.
++     * If the entries were actually committed according to the new leader, our
++     * AppendEntries processing will complete the corresponding commands.
++     */
+     struct raft_server *s;
+     HMAP_FOR_EACH (s, hmap_node, &raft->add_servers) {
+         raft_send_add_server_reply__(raft, &s->sid, s->address, false,
+@@ -2612,8 +2664,6 @@ raft_become_follower(struct raft *raft)
+         raft_server_destroy(raft->remove_server);
+         raft->remove_server = NULL;
+     }
+-
+-    raft_complete_all_commands(raft, RAFT_CMD_LOST_LEADERSHIP);
+ }
+ 
+ static void
+@@ -2826,8 +2876,8 @@ raft_truncate(struct raft *raft, uint64_t new_end)
      return servers_changed;
  }
  
@@ -6882,7 +6995,7 @@ index 2fb5156519..855404808c 100644
  {
      /* Invariant: log_start - 2 <= last_applied <= commit_index < log_end. */
      ovs_assert(raft->log_start <= raft->last_applied + 2);
-@@ -2839,32 +2866,20 @@ raft_peek_next_entry(struct raft *raft, struct uuid *eid)
+@@ -2839,32 +2889,20 @@ raft_peek_next_entry(struct raft *raft, struct uuid *eid)
      }
  
      if (raft->log_start == raft->last_applied + 2) {
@@ -6918,16 +7031,115 @@ index 2fb5156519..855404808c 100644
  /* Updates commit index in raft log. If commit index is already up-to-date
   * it does nothing and return false, otherwise, returns true. */
  static bool
-@@ -2878,7 +2893,7 @@ raft_update_commit_index(struct raft *raft, uint64_t new_commit_index)
-         while (raft->commit_index < new_commit_index) {
-             uint64_t index = ++raft->commit_index;
-             const struct raft_entry *e = raft_get_entry(raft, index);
+@@ -2874,61 +2912,56 @@ raft_update_commit_index(struct raft *raft, uint64_t new_commit_index)
+         return false;
+     }
+ 
+-    if (raft->role == RAFT_LEADER) {
+-        while (raft->commit_index < new_commit_index) {
+-            uint64_t index = ++raft->commit_index;
+-            const struct raft_entry *e = raft_get_entry(raft, index);
 -            if (e->data) {
-+            if (raft_entry_has_data(e)) {
-                 struct raft_command *cmd
-                     = raft_find_command_by_eid(raft, &e->eid);
-                 if (cmd) {
-@@ -3059,7 +3074,9 @@ raft_handle_append_entries(struct raft *raft,
+-                struct raft_command *cmd
+-                    = raft_find_command_by_eid(raft, &e->eid);
+-                if (cmd) {
+-                    if (!cmd->index) {
+-                        VLOG_DBG("Command completed after role change from"
+-                                 " follower to leader "UUID_FMT,
+-                                 UUID_ARGS(&e->eid));
+-                        cmd->index = index;
+-                    }
+-                    raft_command_complete(raft, cmd, RAFT_CMD_SUCCESS);
++    while (raft->commit_index < new_commit_index) {
++        uint64_t index = ++raft->commit_index;
++        const struct raft_entry *e = raft_get_entry(raft, index);
++
++        if (raft_entry_has_data(e)) {
++            struct raft_command *cmd = raft_find_command_by_eid(raft, &e->eid);
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
++
++            if (cmd) {
++                if (!cmd->index && raft->role == RAFT_LEADER) {
++                    VLOG_INFO_RL(&rl,
++                        "command completed after role change from "
++                        "follower to leader (eid: "UUID_FMT", "
++                        "commit index: %"PRIu64")", UUID_ARGS(&e->eid), index);
++                } else if (!cmd->index && raft->role != RAFT_LEADER) {
++                    /* This can happen when leader fail-over before sending
++                     * execute_command_reply. */
++                    VLOG_INFO_RL(&rl,
++                        "command completed without reply (eid: "UUID_FMT", "
++                        "commit index: %"PRIu64")", UUID_ARGS(&e->eid), index);
++                } else if (cmd->index && raft->role != RAFT_LEADER) {
++                    /* This can happen if current server lost leadership after
++                     * sending append requests to the majority of servers, but
++                     * before receiving majority of append replies. */
++                    VLOG_INFO_RL(&rl,
++                        "command completed after role change from "
++                        "leader to follower (eid: "UUID_FMT", "
++                        "commit index: %"PRIu64")", UUID_ARGS(&e->eid), index);
++                    /* Clearing 'sid' to avoid sending cmd execution reply. */
++                    cmd->sid = UUID_ZERO;
++                } else {
++                    /* (cmd->index && raft->role == RAFT_LEADER)
++                     * Normal command completion on a leader. */
+                 }
+-            }
+-            if (e->election_timer) {
+-                VLOG_INFO("Election timer changed from %"PRIu64" to %"PRIu64,
+-                          raft->election_timer, e->election_timer);
+-                raft->election_timer = e->election_timer;
+-                raft->election_timer_new = 0;
+-                raft_update_probe_intervals(raft);
+-            }
+-            if (e->servers) {
+-                /* raft_run_reconfigure() can write a new Raft entry, which can
+-                 * reallocate raft->entries, which would invalidate 'e', so
+-                 * this case must be last, after the one for 'e->data'. */
+-                raft_run_reconfigure(raft);
++                cmd->index = index;
++                raft_command_complete(raft, cmd, RAFT_CMD_SUCCESS);
+             }
+         }
+-    } else {
+-        while (raft->commit_index < new_commit_index) {
+-            uint64_t index = ++raft->commit_index;
+-            const struct raft_entry *e = raft_get_entry(raft, index);
+-            if (e->election_timer) {
+-                VLOG_INFO("Election timer changed from %"PRIu64" to %"PRIu64,
+-                          raft->election_timer, e->election_timer);
+-                raft->election_timer = e->election_timer;
+-                raft_update_probe_intervals(raft);
+-            }
++        if (e->election_timer) {
++            VLOG_INFO("Election timer changed from %"PRIu64" to %"PRIu64,
++                      raft->election_timer, e->election_timer);
++            raft->election_timer = e->election_timer;
++            raft->election_timer_new = 0;
++            raft_update_probe_intervals(raft);
+         }
+-        /* Check if any pending command can be completed, and complete it.
+-         * This can happen when leader fail-over before sending
+-         * execute_command_reply. */
+-        const struct uuid *eid = raft_get_eid(raft, new_commit_index);
+-        struct raft_command *cmd = raft_find_command_by_eid(raft, eid);
+-        if (cmd) {
+-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
+-            VLOG_INFO_RL(&rl,
+-                         "Command completed without reply (eid: "UUID_FMT", "
+-                         "commit index: %"PRIu64")",
+-                         UUID_ARGS(eid), new_commit_index);
+-            cmd->index = new_commit_index;
+-            raft_command_complete(raft, cmd, RAFT_CMD_SUCCESS);
++        if (e->servers && raft->role == RAFT_LEADER) {
++            /* raft_run_reconfigure() can write a new Raft entry, which can
++             * reallocate raft->entries, which would invalidate 'e', so
++             * this case must be last, after the one for 'e->data'. */
++            raft_run_reconfigure(raft);
+         }
+     }
+ 
+@@ -3059,7 +3092,9 @@ raft_handle_append_entries(struct raft *raft,
      for (; i < n_entries; i++) {
          const struct raft_entry *e = &entries[i];
          error = raft_write_entry(raft, e->term,
@@ -6938,7 +7150,7 @@ index 2fb5156519..855404808c 100644
                                   json_nullable_clone(e->servers),
                                   e->election_timer);
          if (error) {
-@@ -3314,20 +3331,29 @@ bool
+@@ -3314,20 +3349,29 @@ bool
  raft_has_next_entry(const struct raft *raft_)
  {
      struct raft *raft = CONST_CAST(struct raft *, raft_);
@@ -6978,7 +7190,7 @@ index 2fb5156519..855404808c 100644
  }
  
  /* Returns the log index of the last-read snapshot or log entry. */
-@@ -3352,12 +3378,6 @@ raft_find_peer(struct raft *raft, const struct uuid *uuid)
+@@ -3352,12 +3396,6 @@ raft_find_peer(struct raft *raft, const struct uuid *uuid)
      return s && !uuid_equals(&raft->sid, &s->sid) ? s : NULL;
  }
  
@@ -6991,7 +7203,7 @@ index 2fb5156519..855404808c 100644
  /* 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)." */
-@@ -3420,6 +3440,7 @@ raft_send_install_snapshot_request(struct raft *raft,
+@@ -3420,6 +3458,7 @@ raft_send_install_snapshot_request(struct raft *raft,
                                     const struct raft_server *s,
                                     const char *comment)
  {
@@ -6999,7 +7211,7 @@ index 2fb5156519..855404808c 100644
      union raft_rpc rpc = {
          .install_snapshot_request = {
              .common = {
-@@ -3432,7 +3453,7 @@ raft_send_install_snapshot_request(struct raft *raft,
+@@ -3432,7 +3471,7 @@ raft_send_install_snapshot_request(struct raft *raft,
              .last_term = raft->snap.term,
              .last_servers = raft->snap.servers,
              .last_eid = raft->snap.eid,
@@ -7008,7 +7220,7 @@ index 2fb5156519..855404808c 100644
              .election_timer = raft->election_timer, /* use latest value */
          }
      };
-@@ -3980,6 +4001,10 @@ raft_write_snapshot(struct raft *raft, struct ovsdb_log *log,
+@@ -3980,6 +4019,10 @@ raft_write_snapshot(struct raft *raft, struct ovsdb_log *log,
                      uint64_t new_log_start,
                      const struct raft_entry *new_snapshot)
  {
@@ -7019,7 +7231,7 @@ index 2fb5156519..855404808c 100644
      struct raft_header h = {
          .sid = raft->sid,
          .cid = raft->cid,
-@@ -3998,12 +4023,13 @@ raft_write_snapshot(struct raft *raft, struct ovsdb_log *log,
+@@ -3998,12 +4041,13 @@ raft_write_snapshot(struct raft *raft, struct ovsdb_log *log,
      /* Write log records. */
      for (uint64_t index = new_log_start; index < raft->log_end; index++) {
          const struct raft_entry *e = &raft->entries[index - raft->log_start];
@@ -7034,7 +7246,7 @@ index 2fb5156519..855404808c 100644
                  .servers = e->servers,
                  .election_timer = e->election_timer,
                  .eid = e->eid,
-@@ -4093,19 +4119,21 @@ raft_handle_install_snapshot_request__(
+@@ -4093,19 +4137,21 @@ raft_handle_install_snapshot_request__(
  
      /* Case 3: The new snapshot starts past the end of our current log, so
       * discard all of our current log. */
@@ -7059,7 +7271,7 @@ index 2fb5156519..855404808c 100644
          return false;
      }
  
-@@ -4120,7 +4148,7 @@ raft_handle_install_snapshot_request__(
+@@ -4120,7 +4166,7 @@ raft_handle_install_snapshot_request__(
      }
  
      raft_entry_uninit(&raft->snap);
@@ -7068,7 +7280,7 @@ index 2fb5156519..855404808c 100644
  
      raft_get_servers_from_log(raft, VLL_INFO);
      raft_get_election_timer_from_log(raft);
-@@ -4132,6 +4160,10 @@ static void
+@@ -4132,6 +4178,10 @@ static void
  raft_handle_install_snapshot_request(
      struct raft *raft, const struct raft_install_snapshot_request *rq)
  {
@@ -7079,7 +7291,7 @@ index 2fb5156519..855404808c 100644
      if (raft_handle_install_snapshot_request__(raft, rq)) {
          union raft_rpc rpy = {
              .install_snapshot_reply = {
-@@ -4216,7 +4248,7 @@ raft_may_snapshot(const struct raft *raft)
+@@ -4216,7 +4266,7 @@ raft_may_snapshot(const struct raft *raft)
              && !raft->leaving
              && !raft->left
              && !raft->failed
@@ -7088,7 +7300,7 @@ index 2fb5156519..855404808c 100644
              && raft->last_applied >= raft->log_start);
  }
  
-@@ -4265,11 +4297,12 @@ raft_store_snapshot(struct raft *raft, const struct json *new_snapshot_data)
+@@ -4265,11 +4315,12 @@ raft_store_snapshot(struct raft *raft, const struct json *new_snapshot_data)
      uint64_t new_log_start = raft->last_applied + 1;
      struct raft_entry new_snapshot = {
          .term = raft_get_term(raft, new_log_start - 1),
@@ -7102,7 +7314,7 @@ index 2fb5156519..855404808c 100644
      struct ovsdb_error *error = raft_save_snapshot(raft, new_log_start,
                                                     &new_snapshot);
      if (error) {
-@@ -4286,6 +4319,9 @@ raft_store_snapshot(struct raft *raft, const struct json *new_snapshot_data)
+@@ -4286,6 +4337,9 @@ raft_store_snapshot(struct raft *raft, const struct json *new_snapshot_data)
      memmove(&raft->entries[0], &raft->entries[new_log_start - raft->log_start],
              (raft->log_end - new_log_start) * sizeof *raft->entries);
      raft->log_start = new_log_start;
@@ -7112,7 +7324,7 @@ index 2fb5156519..855404808c 100644
      return NULL;
  }
  
-@@ -4926,6 +4962,8 @@ raft_unixctl_failure_test(struct unixctl_conn *conn OVS_UNUSED,
+@@ -4926,6 +4980,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;
@@ -7121,6 +7333,18 @@ index 2fb5156519..855404808c 100644
      } else if (!strcmp(test, "delay-election")) {
          failure_test = FT_DELAY_ELECTION;
          struct raft *raft;
+@@ -4938,6 +4994,11 @@ raft_unixctl_failure_test(struct unixctl_conn *conn OVS_UNUSED,
+         failure_test = FT_DONT_SEND_VOTE_REQUEST;
+     } else if (!strcmp(test, "stop-raft-rpc")) {
+         failure_test = FT_STOP_RAFT_RPC;
++    } else if (!strcmp(test,
++                       "transfer-leadership-after-sending-append-request")) {
++        failure_test = FT_TRANSFER_LEADERSHIP_AFTER_SEND_APPEND_REQ;
++    } else if (!strcmp(test, "transfer-leadership")) {
++        failure_test = FT_TRANSFER_LEADERSHIP;
+     } else if (!strcmp(test, "clear")) {
+         failure_test = FT_NO_TEST;
+         unixctl_command_reply(conn, "test dismissed");
 diff --git a/ovsdb/raft.h b/ovsdb/raft.h
 index 3545c41c2c..599bc0ae86 100644
 --- a/ovsdb/raft.h
@@ -8544,6 +8768,81 @@ index 7ef32d13cb..cb0e9df388 100755
      print()
  
      flows.write(struct.pack('>LH',
+diff --git a/tests/mcast-snooping.at b/tests/mcast-snooping.at
+index 757cf7186e..fe475e7b38 100644
+--- a/tests/mcast-snooping.at
++++ b/tests/mcast-snooping.at
+@@ -216,3 +216,70 @@ AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl
+ ])
+ 
+ AT_CLEANUP
++
++
++AT_SETUP([mcast - igmp flood for non-snoop enabled])
++OVS_VSWITCHD_START([])
++
++AT_CHECK([
++    ovs-vsctl set bridge br0 \
++    datapath_type=dummy], [0])
++
++add_of_ports br0 1 2
++
++AT_CHECK([ovs-ofctl add-flow br0 action=normal])
++
++ovs-appctl time/stop
++
++dnl Basic scenario - needs to flood for IGMP followed by unicast ICMP
++dnl in reverse direction
++AT_CHECK([ovs-appctl netdev-dummy/receive p1 \
++    '0101000c29a0aa55aa550001080046c00028000040000102d3494565eb4ae0000016940400002200f9020000000104000000e00000fb000000000000'])
++AT_CHECK([ovs-appctl netdev-dummy/receive p2 \
++    'aa55aa5500010101000c29a008004500001c00010000400164dc0a0101010a0101020800f7ffffffffff'])
++
++
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep -e .*ipv4 | sort | dnl
++          strip_stats | strip_used | strip_recirc | dnl
++          sed -e 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'],
++                     [0], [dnl
++recirc_id(<recirc>),in_port(1),eth(src=aa:55:aa:55:00:01,dst=01:01:00:0c:29:a0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:100,2
++recirc_id(<recirc>),in_port(2),eth(src=01:01:00:0c:29:a0,dst=aa:55:aa:55:00:01),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:1
++])
++
++ovs-appctl time/warp 100000
++
++dnl Next we should clear the flows and install a complex case
++AT_CHECK([ovs-ofctl del-flows br0])
++
++AT_DATA([flows.txt], [dnl
++table=0, arp actions=NORMAL
++table=0, ip,in_port=1 actions=ct(table=1,zone=64000)
++table=0, in_port=2 actions=output:1
++table=1, ip,ct_state=+trk+inv actions=drop
++table=1  ip,in_port=1,icmp,ct_state=+trk+new actions=output:2
++table=1, in_port=1,ip,ct_state=+trk+new actions=controller(userdata=00.de.ad.be.ef.ca.fe.01)
++table=1, in_port=1,ip,ct_state=+trk+est actions=output:2
++])
++AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
++
++ovs-appctl time/warp 100000
++
++dnl Send the IGMP, followed by a unicast ICMP - ensure we won't black hole
++AT_CHECK([ovs-appctl netdev-dummy/receive p1 \
++    '0101000c29a0aa55aa550001080046c00028000040000102d3494565eb4ae0000016940400002200f9020000000104000000e00000fb000000000000'])
++AT_CHECK([ovs-appctl netdev-dummy/receive p1 \
++    'aa55aa550001aa55aa55000208004500001c00010000400164dc0a0101010a0101020800f7ffffffffff'])
++
++
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep -e .*ipv4 | sort | dnl
++          strip_stats | strip_used | strip_recirc | dnl
++          sed 's/pid=[[0-9]]*,//
++               s/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'],
++                     [0], [dnl
++ct_state(+new-inv+trk),recirc_id(<recirc>),in_port(1),eth_type(0x0800),ipv4(proto=1,frag=no), packets:0, bytes:0, used:never, actions:2
++ct_state(+new-inv+trk),recirc_id(<recirc>),in_port(1),eth_type(0x0800),ipv4(proto=2,frag=no), packets:0, bytes:0, used:never, actions:userspace(controller(reason=1,dont_send=0,continuation=0,recirc_id=<recirc>,rule_cookie=0,controller_id=0,max_len=65535))
++recirc_id(<recirc>),in_port(1),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:ct(zone=64000),recirc(<recirc>)
++])
++
++AT_CLEANUP
 diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
 index 956a69e1fa..266ed801e6 100644
 --- a/tests/ofproto-dpif.at
@@ -8889,6 +9188,32 @@ index 956a69e1fa..266ed801e6 100644
  OVS_VSWITCHD_STOP
  AT_CLEANUP
  
+diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
+index 736d9809cb..f906b5c3b5 100644
+--- a/tests/ofproto-macros.at
++++ b/tests/ofproto-macros.at
+@@ -134,6 +134,21 @@ strip_ufid () {
+     sed 's/mega_ufid:[[-0-9a-f]]* //
+     s/ufid:[[-0-9a-f]]* //'
+ }
++
++# Strips packets: and bytes: from output
++strip_stats () {
++    sed 's/packets:[[0-9]]*/packets:0/
++    s/bytes:[[0-9]]*/bytes:0/'
++}
++
++# Changes all 'recirc(...)' and 'recirc=...' to say 'recirc(<recirc_id>)' and
++# 'recirc=<recirc_id>' respectively.  This should make output easier to
++# compare.
++strip_recirc() {
++   sed 's/recirc_id([[x0-9]]*)/recirc_id(<recirc>)/
++        s/recirc_id=[[x0-9]]*/recirc_id=<recirc>/
++        s/recirc([[x0-9]]*)/recirc(<recirc>)/'
++}
+ m4_divert_pop([PREPARE_TESTS])
+ 
+ m4_define([TESTABLE_LOG], [-vPATTERN:ANY:'%c|%p|%m'])
 diff --git a/tests/ovs-macros.at b/tests/ovs-macros.at
 index 66545da572..e6c5bc6e94 100644
 --- a/tests/ovs-macros.at
@@ -8983,7 +9308,7 @@ index 06b671df8c..2d14f1ac26 100644
  AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket db], [0], [ignore], [ignore])
  sed 's/5\.1\.3/5.1.4/' < schema > schema2
 diff --git a/tests/ovsdb-cluster.at b/tests/ovsdb-cluster.at
-index fc6253cfe9..ee9c7b9379 100644
+index fc6253cfe9..0f7076a052 100644
 --- a/tests/ovsdb-cluster.at
 +++ b/tests/ovsdb-cluster.at
 @@ -400,6 +400,61 @@ done
@@ -9048,7 +9373,11 @@ index fc6253cfe9..ee9c7b9379 100644
  
  
  OVS_START_SHELL_HELPERS
-@@ -416,9 +471,8 @@ ovsdb_cluster_failure_test () {
+@@ -413,12 +468,12 @@ ovsdb_cluster_failure_test () {
+     if test "$crash_node" == "1"; then
+         new_leader=$5
+     fi
++    log_grep=$6
  
      cp $top_srcdir/vswitchd/vswitch.ovsschema schema
      schema=`ovsdb-tool schema-name schema`
@@ -9060,7 +9389,57 @@ index fc6253cfe9..ee9c7b9379 100644
  
      n=3
      join_cluster() {
-@@ -629,9 +683,8 @@ ovsdb_torture_test () {
+@@ -434,7 +489,7 @@ ovsdb|WARN|schema: changed 30 columns in 'Open_vSwitch' database from ephemeral
+     start_server() {
+         local i=$1
+         printf "\ns$i: starting\n"
+-        AT_CHECK([ovsdb-server -vjsonrpc -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db])
++        AT_CHECK([ovsdb-server -vjsonrpc -vraft -vconsole:off -vsyslog:off --detach --no-chdir --log-file=s$i.log --pidfile=s$i.pid --unixctl=s$i --remote=punix:s$i.ovsdb s$i.db])
+     }
+     connect_server() {
+         local i=$1
+@@ -460,14 +515,23 @@ ovsdb|WARN|schema: changed 30 columns in 'Open_vSwitch' database from ephemeral
+         fi
+         AT_CHECK([ovs-appctl -t "`pwd`"/s$delay_election_node cluster/failure-test delay-election], [0], [ignore])
+     fi
++
++    # Initializing the database separately to avoid extra 'wait' operation
++    # in later transactions.
++    AT_CHECK([ovs-vsctl -v --db="$db" --no-leader-only --no-shuffle-remotes --no-wait init], [0], [ignore], [ignore])
++
+     AT_CHECK([ovs-appctl -t "`pwd`"/s$crash_node cluster/failure-test $crash_command], [0], [ignore])
+     AT_CHECK([ovs-vsctl -v --db="$db" --no-leader-only --no-shuffle-remotes --no-wait create QoS type=x], [0], [ignore], [ignore])
+ 
+-    # Make sure that the node really crashed.
+-    AT_CHECK([ls s$crash_node.ovsdb], [2], [ignore], [ignore])
+-    # XXX: Client will fail if remotes contains unix socket that doesn't exist (killed).
+-    if test "$remote_1" = "$crash_node"; then
+-        db=unix:s$remote_2.ovsdb
++    # Make sure that the node really crashed or has specific log message.
++    if test -z "$log_grep"; then
++        AT_CHECK([ls s$crash_node.ovsdb], [2], [ignore], [ignore])
++        # XXX: Client will fail if remotes contains unix socket that doesn't exist (killed).
++        if test "$remote_1" = "$crash_node"; then
++            db=unix:s$remote_2.ovsdb
++        fi
++    else
++        OVS_WAIT_UNTIL([grep -q "$log_grep" s${crash_node}.log])
+     fi
+     AT_CHECK([ovs-vsctl --db="$db" --no-leader-only --no-wait --columns=type --bare list QoS], [0], [x
+ ])
+@@ -563,6 +627,11 @@ AT_KEYWORDS([ovsdb server negative unix cluster pending-txn])
+ ovsdb_cluster_failure_test 2 2 3 crash-after-receiving-append-request-update
+ AT_CLEANUP
+ 
++AT_SETUP([OVSDB cluster - txn on leader, leader transfers leadership after sending appendReq])
++AT_KEYWORDS([ovsdb server negative unix cluster pending-txn transfer])
++ovsdb_cluster_failure_test 1 2 1 transfer-leadership-after-sending-append-request -1 "Transferring leadership"
++AT_CLEANUP
++
+ 
+ AT_SETUP([OVSDB cluster - competing candidates])
+ AT_KEYWORDS([ovsdb server negative unix cluster competing-candidates])
+@@ -629,9 +698,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`
@@ -9914,7 +10293,7 @@ index 1714273e35..270956d13f 100644
  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 f400cfabc9..ea3078e3e4 100644
+index f400cfabc9..c6ff310013 100644
 --- a/tests/system-traffic.at
 +++ b/tests/system-traffic.at
 @@ -218,6 +218,7 @@ OVS_TRAFFIC_VSWITCHD_STOP
@@ -10288,6 +10667,92 @@ index f400cfabc9..ea3078e3e4 100644
      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
+@@ -6106,6 +6315,85 @@ AT_CHECK([ovs-ofctl dump-flows br0 | grep table=2, | OFPROTO_CLEAR_DURATION_IDLE
+ OVS_TRAFFIC_VSWITCHD_STOP
+ AT_CLEANUP
+ 
++AT_BANNER([IGMP])
++
++AT_SETUP([IGMP - flood under normal action])
++
++OVS_TRAFFIC_VSWITCHD_START()
++ADD_NAMESPACES(at_ns0, at_ns1)
++
++ADD_VETH(p1, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01")
++ADD_VETH(p2, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02")
++
++AT_CHECK([ovs-ofctl add-flow br0 "actions=NORMAL"])
++
++NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p1 01 00 5e 01 01 03 dnl
++f0 00 00 01 01 01 08 00 46 c0 00 28 00 00 40 00 01 02 d3 49 45 65 eb 4a e0 dnl
++00 00 16 94 04 00 00 22 00 f9 02 00 00 00 01 04 00 00 00 e0 00 00 fb 00 00 dnl
++00 00 00 00 > /dev/null])
++
++AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep -e .*ipv4 | sort | dnl
++          strip_stats | strip_used | strip_recirc | dnl
++          sed 's/,packet_type(ns=[[0-9]]*,id=[[0-9]]*),/,/'],
++                     [0], [dnl
++recirc_id(<recirc>),in_port(ovs-p1),eth(src=f0:00:00:01:01:01,dst=01:00:5e:01:01:03),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:br0,ovs-p2
++])
++OVS_TRAFFIC_VSWITCHD_STOP
++AT_CLEANUP
++
++AT_SETUP([IGMP - forward with ICMP])
++
++OVS_TRAFFIC_VSWITCHD_START()
++ADD_NAMESPACES(at_ns0, at_ns1)
++
++ADD_VETH(p1, at_ns0, br0, "10.1.1.1/24", "f0:00:00:01:01:01")
++ADD_VETH(p2, at_ns1, br0, "10.1.1.2/24", "f0:00:00:01:01:02")
++
++AT_DATA([flows.txt], [dnl
++table=0, arp actions=NORMAL
++table=0, ip,in_port=1 actions=ct(table=1,zone=64000)
++table=0, in_port=2 actions=output:1
++table=1, ip,ct_state=+trk+inv actions=drop
++table=1  ip,in_port=1,icmp,ct_state=+trk+new actions=output:2
++table=1, in_port=1,ip,ct_state=+trk+new actions=controller(userdata=00.de.ad.be.ef.ca.fe.01)
++table=1, in_port=1,ip,ct_state=+trk+est actions=output:2
++])
++AT_CHECK([ovs-ofctl del-flows br0])
++AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
++
++dnl Send the IGMP, followed by a unicast ICMP - ensure we won't black hole
++
++NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p1 f0 00 00 01 01 02 dnl
++f0 00 00 01 01 01 08 00 46 c0 00 28 00 00 40 00 01 02 d3 49 45 65 eb 4a e0 dnl
++00 00 16 94 04 00 00 22 00 f9 02 00 00 00 01 04 00 00 00 e0 00 00 fb 00 00 dnl
++00 00 00 00 > /dev/null])
++
++NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p1 f0 00 00 01 01 02 dnl
++f0 00 00 01 01 01 08 00 45 00 00 1c 00 01 00 00 40 01 64 dc 0a 01 01 01 0a dnl
++01 01 02 08 00 f7 ff ff ff ff ff > /dev/null])
++
++sleep 1
++
++dnl Prefer the OpenFlow rules, because different datapaths will behave slightly
++dnl differently with respect to the exact dp rules.
++dnl
++dnl This is also why we clear n_bytes / n_packets - some kernels with ipv6
++dnl enabled will bump some of these counters non-deterministically
++
++AT_CHECK([ovs-ofctl dump-flows br0 | grep -v NXST | dnl
++          strip_duration | grep -v arp | grep -v n_packets=0 | dnl
++          grep -v 'in_port=2 actions=output:1' | dnl
++          sed 's/n_bytes=[[0-9]]*/n_bytes=0/
++               s/idle_age=[[0-9]]*/idle_age=0/
++               s/n_packets=[[1-9]]/n_packets=0/' | sort], [0], [dnl
++ cookie=0x0,  table=0, n_packets=0, n_bytes=0, idle_age=0, ip,in_port=1 actions=ct(table=1,zone=64000)
++ cookie=0x0,  table=1, n_packets=0, n_bytes=0, idle_age=0, ct_state=+new+trk,icmp,in_port=1 actions=output:2
++ cookie=0x0,  table=1, n_packets=0, n_bytes=0, idle_age=0, ct_state=+new+trk,ip,in_port=1 actions=controller(userdata=00.de.ad.be.ef.ca.fe.01)
++])
++
++OVS_TRAFFIC_VSWITCHD_STOP
++AT_CLEANUP
++
+ AT_BANNER([802.1ad])
+ 
+ AT_SETUP([802.1ad - vlan_limit])
 diff --git a/tests/system-tso-macros.at b/tests/system-tso-macros.at
 index 406334f3e0..1a80047619 100644
 --- a/tests/system-tso-macros.at
diff --git a/SPECS/openvswitch2.16.spec b/SPECS/openvswitch2.16.spec
index 3a5ef72..50275a8 100644
--- a/SPECS/openvswitch2.16.spec
+++ b/SPECS/openvswitch2.16.spec
@@ -57,7 +57,7 @@ Summary: Open vSwitch
 Group: System Environment/Daemons daemon/database/utilities
 URL: http://www.openvswitch.org/
 Version: 2.16.0
-Release: 77%{?dist}
+Release: 78%{?dist}
 
 # Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
 # lib/sflow*.[ch] files are SISSL
@@ -699,6 +699,14 @@ exit 0
 %endif
 
 %changelog
+* Thu May 26 2022 Ilya Maximets <i.maximets@redhat.com> - 2.16.0-78
+- Merging upstream branch-2.16 [RH git: d7d5f09849]
+    Commit list:
+    c8c78a76e5 ovsdb: raft: Fix transaction double commit due to lost leadership. (#2046340)
+    2809af022a Revert "odp-util: Always report ODP_FIT_TOO_LITTLE for IGMP."
+    90e31552be ofproto-dpif: Trigger revalidation if ct tp changes.
+
+
 * Wed May 25 2022 Open vSwitch CI <ovs-ci@redhat.com> - 2.16.0-77
 - Merging upstream branch-2.16 [RH git: 3e3d3725d3]
     Commit list: