diff --git a/SOURCES/openvswitch-2.17.0.patch b/SOURCES/openvswitch-2.17.0.patch
index 0e63353..99cb42d 100644
--- a/SOURCES/openvswitch-2.17.0.patch
+++ b/SOURCES/openvswitch-2.17.0.patch
@@ -336,13 +336,19 @@ index 27be4aa412..1dc406170f 100644
 +.. |emeritus-status|  replace:: `Emeritus Status for OVS Committers
 +   <Documentation/internals/committer-emeritus-status.rst>`__
 diff --git a/NEWS b/NEWS
-index c10e9bfacc..45b974ed22 100644
+index c10e9bfacc..095b0574f4 100644
 --- a/NEWS
 +++ b/NEWS
-@@ -1,3 +1,69 @@
-+v2.17.6 - xx xxx xxxx
+@@ -1,3 +1,75 @@
++v2.17.7 - xx xxx xxxx
 +---------------------
 +
++v2.17.6 - 06 Apr 2023
++---------------------
++   - Bug fixes
++   - Security:
++     * Fixed vulnerability CVE-2023-1668.
++
 +v2.17.5 - 20 Dec 2022
 +---------------------
 +   - Bug fixes
@@ -611,7 +617,7 @@ index 1884c99e1f..aca1dbca91 100755
 +dhparam_to_c lib/dh4096.pem
 +echo "#endif"
 diff --git a/configure.ac b/configure.ac
-index 4e9bcce272..64db07f27b 100644
+index 4e9bcce272..4b8c162067 100644
 --- a/configure.ac
 +++ b/configure.ac
 @@ -13,7 +13,7 @@
@@ -619,7 +625,7 @@ index 4e9bcce272..64db07f27b 100644
  
  AC_PREREQ(2.63)
 -AC_INIT(openvswitch, 2.17.0, bugs@openvswitch.org)
-+AC_INIT(openvswitch, 2.17.6, bugs@openvswitch.org)
++AC_INIT(openvswitch, 2.17.7, bugs@openvswitch.org)
  AC_CONFIG_SRCDIR([datapath/datapath.c])
  AC_CONFIG_MACRO_DIR([m4])
  AC_CONFIG_AUX_DIR([build-aux])
@@ -871,15 +877,21 @@ index cc0840704a..2a206305ec 100644
          completionList->dropNbl = NULL;
          completionList->dropNblNext = &completionList->dropNbl;
 diff --git a/debian/changelog b/debian/changelog
-index 3e0d3a66e3..11de722a81 100644
+index 3e0d3a66e3..0c1940c6e7 100644
 --- a/debian/changelog
 +++ b/debian/changelog
-@@ -1,3 +1,39 @@
+@@ -1,3 +1,45 @@
++openvswitch (2.17.7-1) unstable; urgency=low
++   [ Open vSwitch team ]
++   * New upstream version
++
++ -- Open vSwitch team <dev@openvswitch.org>  Thu, 06 Apr 2023 15:09:54 +0200
++
 +openvswitch (2.17.6-1) unstable; urgency=low
 +   [ Open vSwitch team ]
 +   * New upstream version
 +
-+ -- Open vSwitch team <dev@openvswitch.org>  Tue, 20 Dec 2022 20:06:56 +0100
++ -- Open vSwitch team <dev@openvswitch.org>  Thu, 06 Apr 2023 15:09:54 +0200
 +
 +openvswitch (2.17.5-1) unstable; urgency=low
 +   [ Open vSwitch team ]
@@ -51108,6 +51120,26 @@ index d3b4601858..7f3e63c384 100644
  }
  
  static bool
+diff --git a/lib/conntrack-tp.h b/lib/conntrack-tp.h
+index 4d411d19fd..7ece2eae2f 100644
+--- a/lib/conntrack-tp.h
++++ b/lib/conntrack-tp.h
+@@ -17,8 +17,15 @@
+ #ifndef CONNTRACK_TP_H
+ #define CONNTRACK_TP_H 1
+ 
++#include <stdint.h>
++
+ #define CT_DPIF_NETDEV_TP_MIN 30
++
+ enum ct_timeout;
++struct conn;
++struct conntrack;
++struct timeout_policy;
++
+ void timeout_policy_init(struct conntrack *ct);
+ int timeout_policy_update(struct conntrack *ct, struct timeout_policy *tp);
+ int timeout_policy_delete(struct conntrack *ct, uint32_t tp_id);
 diff --git a/lib/conntrack.c b/lib/conntrack.c
 index 33a1a92953..fff8e77db1 100644
 --- a/lib/conntrack.c
@@ -54261,7 +54293,7 @@ index 94dc6a9b74..303b99daf4 100644
              .queue = rss_data->queue,
              .key_len = 0,
 diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
-index 9845e8d3fe..efb92d5dd7 100644
+index 9845e8d3fe..6d918aebd6 100644
 --- a/lib/netdev-offload-tc.c
 +++ b/lib/netdev-offload-tc.c
 @@ -44,6 +44,7 @@
@@ -54303,7 +54335,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  };
  
  static void
-@@ -198,13 +209,39 @@ del_ufid_tc_mapping(const ovs_u128 *ufid)
+@@ -198,15 +209,42 @@ del_ufid_tc_mapping(const ovs_u128 *ufid)
      ovs_mutex_unlock(&ufid_lock);
  }
  
@@ -54329,6 +54361,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      int err;
  
 -    err = tc_del_filter(id);
+-    if (!err) {
 +    if (stats) {
 +        memset(stats, 0, sizeof *stats);
 +        if (!tc_get_flower(id, &flower)) {
@@ -54342,10 +54375,13 @@ index 9845e8d3fe..efb92d5dd7 100644
 +    }
 +
 +    err = tc_del_flower_filter(id);
-     if (!err) {
++    if (!err || err == ENODEV) {
          del_ufid_tc_mapping(ufid);
++        return 0;
      }
-@@ -214,7 +251,7 @@ del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid)
+     return err;
+ }
+@@ -214,7 +252,7 @@ del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid)
  /* Add ufid entry to ufid_to_tc hashmap. */
  static void
  add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid,
@@ -54354,7 +54390,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  {
      struct ufid_tc_data *new_data = xzalloc(sizeof *new_data);
      size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
-@@ -226,6 +263,9 @@ add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid,
+@@ -226,6 +264,9 @@ add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid,
      new_data->ufid = *ufid;
      new_data->id = *id;
      new_data->netdev = netdev_ref(netdev);
@@ -54364,7 +54400,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  
      ovs_mutex_lock(&ufid_lock);
      hmap_insert(&ufid_to_tc, &new_data->ufid_to_tc_node, ufid_hash);
-@@ -257,6 +297,30 @@ get_ufid_tc_mapping(const ovs_u128 *ufid, struct tcf_id *id)
+@@ -257,6 +298,30 @@ get_ufid_tc_mapping(const ovs_u128 *ufid, struct tcf_id *id)
      return ENOENT;
  }
  
@@ -54395,7 +54431,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  /* Find ufid entry in ufid_to_tc hashmap using tcf_id id.
   * The result is saved in ufid.
   *
-@@ -405,7 +469,7 @@ delete_chains_from_netdev(struct netdev *netdev, struct tcf_id *id)
+@@ -405,7 +470,7 @@ delete_chains_from_netdev(struct netdev *netdev, struct tcf_id *id)
           */
          HMAP_FOR_EACH_POP (chain_node, node, &map) {
              id->chain = chain_node->chain;
@@ -54404,7 +54440,7 @@ index 9845e8d3fe..efb92d5dd7 100644
              free(chain_node);
          }
      }
-@@ -417,16 +481,16 @@ delete_chains_from_netdev(struct netdev *netdev, struct tcf_id *id)
+@@ -417,16 +482,16 @@ delete_chains_from_netdev(struct netdev *netdev, struct tcf_id *id)
  static int
  netdev_tc_flow_flush(struct netdev *netdev)
  {
@@ -54424,7 +54460,7 @@ index 9845e8d3fe..efb92d5dd7 100644
          if (!err) {
              del_ufid_tc_mapping_unlocked(&data->ufid);
          }
-@@ -481,10 +545,10 @@ netdev_tc_flow_dump_destroy(struct netdev_flow_dump *dump)
+@@ -481,10 +546,10 @@ netdev_tc_flow_dump_destroy(struct netdev_flow_dump *dump)
  
  static void
  parse_flower_rewrite_to_netlink_action(struct ofpbuf *buf,
@@ -54438,7 +54474,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  
      for (int type = 0; type < ARRAY_SIZE(set_flower_map); type++) {
          char *put = NULL;
-@@ -550,30 +614,42 @@ flower_tun_opt_to_match(struct match *match, struct tc_flower *flower)
+@@ -550,30 +615,42 @@ flower_tun_opt_to_match(struct match *match, struct tc_flower *flower)
      struct geneve_opt *opt, *opt_mask;
      int len, cnt = 0;
  
@@ -54488,7 +54524,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  }
  
  static void
-@@ -585,8 +661,10 @@ parse_tc_flower_to_stats(struct tc_flower *flower,
+@@ -585,8 +662,10 @@ parse_tc_flower_to_stats(struct tc_flower *flower,
      }
  
      memset(stats, 0, sizeof *stats);
@@ -54501,7 +54537,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      stats->used = flower->lastused;
  }
  
-@@ -616,7 +694,8 @@ parse_tc_flower_terse_to_match(struct tc_flower *flower,
+@@ -616,7 +695,8 @@ parse_tc_flower_terse_to_match(struct tc_flower *flower,
  }
  
  static int
@@ -54511,7 +54547,7 @@ index 9845e8d3fe..efb92d5dd7 100644
                           struct match *match,
                           struct nlattr **actions,
                           struct dpif_flow_stats *stats,
-@@ -803,18 +882,24 @@ parse_tc_flower_to_match(struct tc_flower *flower,
+@@ -803,18 +883,24 @@ parse_tc_flower_to_match(struct tc_flower *flower,
                                            &flower->key.tunnel.ipv6.ipv6_src,
                                            &flower->mask.tunnel.ipv6.ipv6_src);
          }
@@ -54530,18 +54566,18 @@ index 9845e8d3fe..efb92d5dd7 100644
 +        if (flower->mask.tunnel.tp_src) {
 +            match_set_tun_tp_dst_masked(match, flower->key.tunnel.tp_src,
 +                                        flower->mask.tunnel.tp_src);
-         }
--        if (flower->key.tunnel.metadata.present.len) {
++        }
 +        if (flower->mask.tunnel.tp_dst) {
 +            match_set_tun_tp_dst_masked(match, flower->key.tunnel.tp_dst,
 +                                        flower->mask.tunnel.tp_dst);
-+        }
+         }
+-        if (flower->key.tunnel.metadata.present.len) {
 +
 +        if (!strcmp(netdev_get_type(netdev), "geneve")) {
              flower_tun_opt_to_match(match, flower);
          }
      }
-@@ -877,7 +962,7 @@ parse_tc_flower_to_match(struct tc_flower *flower,
+@@ -877,7 +963,7 @@ parse_tc_flower_to_match(struct tc_flower *flower,
              }
              break;
              case TC_ACT_PEDIT: {
@@ -54550,7 +54586,7 @@ index 9845e8d3fe..efb92d5dd7 100644
              }
              break;
              case TC_ACT_ENCAP: {
-@@ -947,7 +1032,11 @@ parse_tc_flower_to_match(struct tc_flower *flower,
+@@ -947,7 +1033,11 @@ parse_tc_flower_to_match(struct tc_flower *flower,
                  ct_offset = nl_msg_start_nested(buf, OVS_ACTION_ATTR_CT);
  
                  if (action->ct.commit) {
@@ -54563,7 +54599,7 @@ index 9845e8d3fe..efb92d5dd7 100644
                  }
  
                  if (action->ct.zone) {
-@@ -965,13 +1054,13 @@ parse_tc_flower_to_match(struct tc_flower *flower,
+@@ -965,13 +1055,13 @@ parse_tc_flower_to_match(struct tc_flower *flower,
                      struct {
                          ovs_u128 key;
                          ovs_u128 mask;
@@ -54583,7 +54619,7 @@ index 9845e8d3fe..efb92d5dd7 100644
                  }
  
                  if (action->ct.nat_type) {
-@@ -1048,23 +1137,28 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
+@@ -1048,23 +1138,28 @@ netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
                          get_tc_qdisc_hook(netdev));
  
      while (nl_dump_next(dump->nl_dump, &nl_flow, rbuffer)) {
@@ -54616,7 +54652,7 @@ index 9845e8d3fe..efb92d5dd7 100644
          match->wc.masks.in_port.odp_port = u32_to_odp(UINT32_MAX);
          match->flow.in_port.odp_port = dump->port;
          match_set_recirc_id(match, id.chain);
-@@ -1164,7 +1258,12 @@ parse_put_flow_ct_action(struct tc_flower *flower,
+@@ -1164,7 +1259,12 @@ parse_put_flow_ct_action(struct tc_flower *flower,
          NL_ATTR_FOR_EACH_UNSAFE (ct_attr, ct_left, ct, ct_len) {
              switch (nl_attr_type(ct_attr)) {
                  case OVS_CT_ATTR_COMMIT: {
@@ -54630,7 +54666,7 @@ index 9845e8d3fe..efb92d5dd7 100644
                  }
                  break;
                  case OVS_CT_ATTR_ZONE: {
-@@ -1194,15 +1293,20 @@ parse_put_flow_ct_action(struct tc_flower *flower,
+@@ -1194,15 +1294,20 @@ parse_put_flow_ct_action(struct tc_flower *flower,
                  break;
                  case OVS_CT_ATTR_LABELS: {
                      const struct {
@@ -54655,7 +54691,7 @@ index 9845e8d3fe..efb92d5dd7 100644
              }
          }
  
-@@ -1222,8 +1326,8 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
+@@ -1222,8 +1327,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;
@@ -54666,7 +54702,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      const struct nlattr *attr;
      int i, j, type;
      size_t size;
-@@ -1265,14 +1369,6 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
+@@ -1265,14 +1370,6 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
          }
      }
  
@@ -54681,7 +54717,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      if (hasmask && !is_all_zeros(set_mask, size)) {
          VLOG_DBG_RL(&rl, "unsupported sub attribute of set action type %d",
                      type);
-@@ -1281,6 +1377,8 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
+@@ -1281,6 +1378,8 @@ parse_put_flow_set_masked_action(struct tc_flower *flower,
      }
  
      ofpbuf_uninit(&set_buf);
@@ -54690,7 +54726,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      return 0;
  }
  
-@@ -1288,6 +1386,7 @@ static int
+@@ -1288,6 +1387,7 @@ static int
  parse_put_flow_set_action(struct tc_flower *flower, struct tc_action *action,
                            const struct nlattr *set, size_t set_len)
  {
@@ -54698,7 +54734,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      const struct nlattr *tunnel;
      const struct nlattr *tun_attr;
      size_t tun_left, tunnel_len;
-@@ -1306,6 +1405,7 @@ parse_put_flow_set_action(struct tc_flower *flower, struct tc_action *action,
+@@ -1306,6 +1406,7 @@ parse_put_flow_set_action(struct tc_flower *flower, struct tc_action *action,
  
      action->type = TC_ACT_ENCAP;
      action->encap.id_present = false;
@@ -54706,7 +54742,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      flower->action_count++;
      NL_ATTR_FOR_EACH_UNSAFE(tun_attr, tun_left, tunnel, tunnel_len) {
          switch (nl_attr_type(tun_attr)) {
-@@ -1330,6 +1430,18 @@ parse_put_flow_set_action(struct tc_flower *flower, struct tc_action *action,
+@@ -1330,6 +1431,18 @@ parse_put_flow_set_action(struct tc_flower *flower, struct tc_action *action,
              action->encap.ttl = nl_attr_get_u8(tun_attr);
          }
          break;
@@ -54725,7 +54761,7 @@ index 9845e8d3fe..efb92d5dd7 100644
          case OVS_TUNNEL_KEY_ATTR_IPV6_SRC: {
              action->encap.ipv6.ipv6_src =
                  nl_attr_get_in6_addr(tun_attr);
-@@ -1354,12 +1466,31 @@ parse_put_flow_set_action(struct tc_flower *flower, struct tc_action *action,
+@@ -1354,12 +1467,31 @@ parse_put_flow_set_action(struct tc_flower *flower, struct tc_action *action,
              action->encap.data.present.len = nl_attr_get_size(tun_attr);
          }
          break;
@@ -54757,7 +54793,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  static int
  test_key_and_mask(struct match *match)
  {
-@@ -1442,8 +1573,23 @@ test_key_and_mask(struct match *match)
+@@ -1442,8 +1574,23 @@ test_key_and_mask(struct match *match)
          return EOPNOTSUPP;
      }
  
@@ -54782,7 +54818,7 @@ index 9845e8d3fe..efb92d5dd7 100644
          return EOPNOTSUPP;
      }
  
-@@ -1452,18 +1598,51 @@ test_key_and_mask(struct match *match)
+@@ -1452,18 +1599,51 @@ test_key_and_mask(struct match *match)
  
  static void
  flower_match_to_tun_opt(struct tc_flower *flower, const struct flow_tnl *tnl,
@@ -54820,11 +54856,11 @@ index 9845e8d3fe..efb92d5dd7 100644
 +    flower->mask.tunnel.metadata.present.len = tnl->metadata.present.len;
 +    memset(&tnl_mask->metadata.present.len, 0,
 +           sizeof tnl_mask->metadata.present.len);
- 
++
 +    if (!tnl->metadata.present.len) {
 +        return;
 +    }
-+
+ 
 +    memcpy(flower->key.tunnel.metadata.opts.gnv, tnl->metadata.opts.gnv,
 +           tnl->metadata.present.len);
      memcpy(flower->mask.tunnel.metadata.opts.gnv, tnl_mask->metadata.opts.gnv,
@@ -54837,7 +54873,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      len = flower->key.tunnel.metadata.present.len;
      while (len) {
          opt = &flower->key.tunnel.metadata.opts.gnv[cnt];
-@@ -1474,8 +1653,6 @@ flower_match_to_tun_opt(struct tc_flower *flower, const struct flow_tnl *tnl,
+@@ -1474,8 +1654,6 @@ flower_match_to_tun_opt(struct tc_flower *flower, const struct flow_tnl *tnl,
          cnt += sizeof(struct geneve_opt) / 4 + opt->length;
          len -= sizeof(struct geneve_opt) + opt->length * 4;
      }
@@ -54846,7 +54882,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  }
  
  static void
-@@ -1541,6 +1718,12 @@ parse_match_ct_state_to_flower(struct tc_flower *flower, struct match *match)
+@@ -1541,6 +1719,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);
          }
@@ -54859,7 +54895,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      }
  
      if (mask->ct_zone) {
-@@ -1574,7 +1757,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
+@@ -1574,7 +1758,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
      const struct flow *key = &match->flow;
      struct flow *mask = &match->wc.masks;
      const struct flow_tnl *tnl = &match->flow.tunnel;
@@ -54869,7 +54905,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      struct tc_action *action;
      bool recirc_act = false;
      uint32_t block_id = 0;
-@@ -1615,17 +1799,49 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
+@@ -1615,17 +1800,49 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
          flower.key.tunnel.ttl = tnl->ip_ttl;
          flower.key.tunnel.tp_src = tnl->tp_src;
          flower.key.tunnel.tp_dst = tnl->tp_dst;
@@ -54921,7 +54957,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  
      flower.key.eth_type = key->dl_type;
      flower.mask.eth_type = mask->dl_type;
-@@ -1638,7 +1854,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
+@@ -1638,7 +1855,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
  
      if (mask->vlans[0].tpid && eth_type_vlan(key->vlans[0].tpid)) {
          flower.key.encap_eth_type[0] = flower.key.eth_type;
@@ -54930,7 +54966,7 @@ index 9845e8d3fe..efb92d5dd7 100644
          flower.key.eth_type = key->vlans[0].tpid;
          flower.mask.eth_type = mask->vlans[0].tpid;
      }
-@@ -1734,7 +1950,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
+@@ -1734,7 +1951,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
              memset(&mask->arp_tha, 0, sizeof mask->arp_tha);
      }
  
@@ -54939,7 +54975,7 @@ index 9845e8d3fe..efb92d5dd7 100644
          flower.key.ip_proto = key->nw_proto;
          flower.mask.ip_proto = mask->nw_proto;
          mask->nw_proto = 0;
-@@ -1841,7 +2057,25 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
+@@ -1841,7 +2058,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;
              }
@@ -54965,7 +55001,7 @@ index 9845e8d3fe..efb92d5dd7 100644
              action->out.ingress = is_internal_port(netdev_get_type(outdev));
              action->type = TC_ACT_OUTPUT;
              flower.action_count++;
-@@ -1879,10 +2113,6 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
+@@ -1879,10 +2114,6 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
              if (err) {
                  return err;
              }
@@ -54976,7 +55012,7 @@ index 9845e8d3fe..efb92d5dd7 100644
          } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SET_MASKED) {
              const struct nlattr *set = nl_attr_get(nla);
              const size_t set_len = nl_attr_get_size(nla);
-@@ -1929,10 +2159,12 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
+@@ -1929,10 +2160,12 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
          return EOPNOTSUPP;
      }
  
@@ -54990,7 +55026,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      }
  
      prio = get_prio_for_tc_flower(&flower);
-@@ -1950,8 +2182,9 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
+@@ -1950,8 +2183,9 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
      if (!err) {
          if (stats) {
              memset(stats, 0, sizeof *stats);
@@ -55001,7 +55037,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      }
  
      return err;
-@@ -1989,8 +2222,16 @@ netdev_tc_flow_get(struct netdev *netdev,
+@@ -1989,8 +2223,16 @@ netdev_tc_flow_get(struct netdev *netdev,
      }
  
      in_port = netdev_ifindex_to_odp_port(id.ifindex);
@@ -55019,7 +55055,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      match->wc.masks.in_port.odp_port = u32_to_odp(UINT32_MAX);
      match->flow.in_port.odp_port = in_port;
      match_set_recirc_id(match, id.chain);
-@@ -2003,7 +2244,6 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
+@@ -2003,7 +2245,6 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
                     const ovs_u128 *ufid,
                     struct dpif_flow_stats *stats)
  {
@@ -55027,7 +55063,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      struct tcf_id id;
      int error;
  
-@@ -2012,18 +2252,7 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
+@@ -2012,18 +2253,7 @@ netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
          return error;
      }
  
@@ -55047,7 +55083,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  }
  
  static int
-@@ -2077,13 +2306,13 @@ probe_multi_mask_per_prio(int ifindex)
+@@ -2077,13 +2307,13 @@ probe_multi_mask_per_prio(int ifindex)
  
      id2 = tc_make_tcf_id(ifindex, block_id, prio, TC_INGRESS);
      error = tc_replace_flower(&id2, &flower);
@@ -55063,7 +55099,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  
      multi_mask_per_prio = true;
      VLOG_INFO("probe tc: multiple masks on single tc prio is supported.");
-@@ -2135,7 +2364,7 @@ probe_ct_state_support(int ifindex)
+@@ -2135,7 +2365,7 @@ probe_ct_state_support(int ifindex)
          goto out_del;
      }
  
@@ -55072,7 +55108,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      ct_state_support = OVS_CS_F_NEW |
                         OVS_CS_F_ESTABLISHED |
                         OVS_CS_F_TRACKED |
-@@ -2149,7 +2378,7 @@ probe_ct_state_support(int ifindex)
+@@ -2149,7 +2379,7 @@ probe_ct_state_support(int ifindex)
          goto out_del;
      }
  
@@ -55081,7 +55117,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  
      /* Test for ct_state INVALID support */
      memset(&flower, 0, sizeof flower);
-@@ -2160,7 +2389,7 @@ probe_ct_state_support(int ifindex)
+@@ -2160,7 +2390,7 @@ probe_ct_state_support(int ifindex)
          goto out;
      }
  
@@ -55090,7 +55126,7 @@ index 9845e8d3fe..efb92d5dd7 100644
      ct_state_support |= OVS_CS_F_INVALID;
  
      /* Test for ct_state REPLY support */
-@@ -2176,7 +2405,7 @@ probe_ct_state_support(int ifindex)
+@@ -2176,7 +2406,7 @@ probe_ct_state_support(int ifindex)
      ct_state_support |= OVS_CS_F_REPLY_DIR;
  
  out_del:
@@ -55099,7 +55135,7 @@ index 9845e8d3fe..efb92d5dd7 100644
  out:
      tc_add_del_qdisc(ifindex, false, 0, TC_INGRESS);
      VLOG_INFO("probe tc: supported ovs ct_state bits: 0x%x", ct_state_support);
-@@ -2251,7 +2480,7 @@ netdev_tc_init_flow_api(struct netdev *netdev)
+@@ -2251,7 +2481,7 @@ netdev_tc_init_flow_api(struct netdev *netdev)
  
      /* fallback here if delete chains fail */
      if (!get_chain_supported) {
@@ -60983,6 +61019,48 @@ index 0000000000..b502aea4cf
 +    </group>
 +  </table>
 +</database>
+diff --git a/ovsdb/log.c b/ovsdb/log.c
+index 4a28fa3db6..981d33cc57 100644
+--- a/ovsdb/log.c
++++ b/ovsdb/log.c
+@@ -551,6 +551,23 @@ ovsdb_log_truncate(struct ovsdb_log *file)
+     return error;
+ }
+ 
++/* Removes all the data from the log by moving current offset to zero and
++ * truncating the file to zero bytes.  After this operation the file is empty
++ * and in a write state. */
++struct ovsdb_error * OVS_WARN_UNUSED_RESULT
++ovsdb_log_reset(struct ovsdb_log *file)
++{
++    ovsdb_error_destroy(file->error);
++    file->offset = file->prev_offset = 0;
++    file->error = ovsdb_log_truncate(file);
++    if (file->error) {
++        file->state = OVSDB_LOG_WRITE_ERROR;
++        return ovsdb_error_clone(file->error);
++    }
++    file->state = OVSDB_LOG_WRITE;
++    return NULL;
++}
++
+ /* Composes a log record for 'json' by filling 'header' with a header line and
+  * 'data' with a data line (each ending with a new-line).  To write the record
+  * to a file, write 'header' followed by 'data'.
+diff --git a/ovsdb/log.h b/ovsdb/log.h
+index 90714ea131..63e5681a0b 100644
+--- a/ovsdb/log.h
++++ b/ovsdb/log.h
+@@ -66,6 +66,9 @@ struct ovsdb_error *ovsdb_log_read(struct ovsdb_log *, struct json **)
+     OVS_WARN_UNUSED_RESULT;
+ void ovsdb_log_unread(struct ovsdb_log *);
+ 
++struct ovsdb_error *ovsdb_log_reset(struct ovsdb_log *)
++    OVS_WARN_UNUSED_RESULT;
++
+ void ovsdb_log_compose_record(const struct json *, const char *magic,
+                               struct ds *header, struct ds *data);
+ 
 diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c
 index 0f222cc992..952fa902e4 100644
 --- a/ovsdb/monitor.c
@@ -61129,7 +61207,7 @@ index 10a70ae26f..13c5359395 100755
  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
+index 9fe90592ea..ddb623dc1a 100644
 --- a/ovsdb/ovsdb-server.c
 +++ b/ovsdb/ovsdb-server.c
 @@ -26,6 +26,7 @@
@@ -61184,7 +61262,123 @@ index 9fe90592ea..774416fc7a 100644
      perf_counters_destroy();
      service_stop();
      return 0;
-@@ -1240,8 +1242,8 @@ update_server_status(struct shash *all_dbs)
+@@ -562,8 +564,9 @@ close_db(struct server_config *config, struct db *db, char *comment)
+     }
+ }
+ 
+-static void
+-update_schema(struct ovsdb *db, const struct ovsdb_schema *schema, void *aux)
++static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
++update_schema(struct ovsdb *db, const struct ovsdb_schema *schema,
++              bool conversion_with_no_data, void *aux)
+ {
+     struct server_config *config = aux;
+ 
+@@ -575,13 +578,27 @@ update_schema(struct ovsdb *db, const struct ovsdb_schema *schema, void *aux)
+             : xasprintf("database %s connected to storage", db->name)));
+     }
+ 
+-    ovsdb_replace(db, ovsdb_create(ovsdb_schema_clone(schema), NULL));
++    if (db->schema && conversion_with_no_data) {
++        struct ovsdb *new_db = NULL;
++        struct ovsdb_error *error;
++
++        error = ovsdb_convert(db, schema, &new_db);
++        if (error) {
++            /* Should never happen, because conversion should have been
++             * checked before writing the schema to the storage. */
++            return error;
++        }
++        ovsdb_replace(db, new_db);
++    } else {
++        ovsdb_replace(db, ovsdb_create(ovsdb_schema_clone(schema), NULL));
++    }
+ 
+     /* Force update to schema in _Server database. */
+     struct db *dbp = shash_find_data(config->all_dbs, db->name);
+     if (dbp) {
+         dbp->row_uuid = UUID_ZERO;
+     }
++    return NULL;
+ }
+ 
+ static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
+@@ -589,23 +606,30 @@ parse_txn(struct server_config *config, struct db *db,
+           const struct ovsdb_schema *schema, const struct json *txn_json,
+           const struct uuid *txnid)
+ {
++    struct ovsdb_error *error = NULL;
++    struct ovsdb_txn *txn = NULL;
++
+     if (schema) {
+-        /* We're replacing the schema (and the data).  Destroy the database
+-         * (first grabbing its storage), then replace it with the new schema.
+-         * The transaction must also include the replacement data.
++        /* We're replacing the schema (and the data).  If transaction includes
++         * replacement data, destroy the database (first grabbing its storage),
++         * then replace it with the new schema.  If not, it's a conversion
++         * without data specified.  In this case, convert the current database
++         * to a new schema instead.
+          *
+          * Only clustered database schema changes and snapshot installs
+          * go through this path.
+          */
+-        ovs_assert(txn_json);
+         ovs_assert(ovsdb_storage_is_clustered(db->db->storage));
+ 
+-        struct ovsdb_error *error = ovsdb_schema_check_for_ephemeral_columns(
+-            schema);
++        error = ovsdb_schema_check_for_ephemeral_columns(schema);
++        if (error) {
++            return error;
++        }
++
++        error = update_schema(db->db, schema, txn_json == NULL, config);
+         if (error) {
+             return error;
+         }
+-        update_schema(db->db, schema, config);
+     }
+ 
+     if (txn_json) {
+@@ -613,24 +637,25 @@ parse_txn(struct server_config *config, struct db *db,
+             return ovsdb_error(NULL, "%s: data without schema", db->filename);
+         }
+ 
+-        struct ovsdb_txn *txn;
+-        struct ovsdb_error *error;
+-
+         error = ovsdb_file_txn_from_json(db->db, txn_json, false, &txn);
+-        if (!error) {
+-            ovsdb_txn_set_txnid(txnid, txn);
+-            log_and_free_error(ovsdb_txn_replay_commit(txn));
+-        }
+-        if (!error && !uuid_is_zero(txnid)) {
+-            db->db->prereq = *txnid;
+-        }
+         if (error) {
+             ovsdb_storage_unread(db->db->storage);
+             return error;
+         }
++    } else if (schema) {
++        /* We just performed conversion without data.  Transaction history
++         * was destroyed.  Commit a dummy transaction to set the txnid. */
++        txn = ovsdb_txn_create(db->db);
+     }
+ 
+-    return NULL;
++    if (txn) {
++        ovsdb_txn_set_txnid(txnid, txn);
++        error = ovsdb_txn_replay_commit(txn);
++        if (!error && !uuid_is_zero(txnid)) {
++            db->db->prereq = *txnid;
++        }
++    }
++    return error;
+ }
+ 
+ static void
+@@ -1240,8 +1265,8 @@ update_server_status(struct shash *all_dbs)
  
      /* Update rows for databases that still exist.
       * Delete rows for databases that no longer exist. */
@@ -61196,10 +61390,61 @@ index 9fe90592ea..774416fc7a 100644
          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
+index d4a9e34cc4..f9a423fba2 100644
 --- a/ovsdb/ovsdb-tool.c
 +++ b/ovsdb/ovsdb-tool.c
-@@ -1579,15 +1579,14 @@ do_check_cluster(struct ovs_cmdl_context *ctx)
+@@ -1005,7 +1005,8 @@ raft_header_to_standalone_log(const struct raft_header *h,
+ }
+ 
+ static void
+-raft_record_to_standalone_log(const struct raft_record *r,
++raft_record_to_standalone_log(const char *db_file_name,
++                              const struct raft_record *r,
+                               struct ovsdb_log *db_log_data)
+ {
+     if (r->type == RAFT_REC_ENTRY) {
+@@ -1017,7 +1018,40 @@ raft_record_to_standalone_log(const struct raft_record *r,
+         if (pa->n != 2) {
+             ovs_fatal(0, "Incorrect raft record array length");
+         }
++
++        struct json *schema_json = pa->elems[0];
+         struct json *data_json = pa->elems[1];
++
++        if (schema_json->type != JSON_NULL) {
++            /* This is a database conversion record.  Reset the log and
++             * write the new schema. */
++            struct ovsdb_schema *schema;
++
++            check_ovsdb_error(ovsdb_schema_from_json(schema_json, &schema));
++
++            if (data_json->type == JSON_NULL) {
++                /* We have a conversion request with no data.  There is no
++                 * other way as to read back what we have and convert. */
++                struct ovsdb *old_db, *new_db;
++
++                check_ovsdb_error(ovsdb_log_commit_block(db_log_data));
++
++                old_db = ovsdb_file_read(db_file_name, false);
++                check_ovsdb_error(ovsdb_convert(old_db, schema, &new_db));
++                ovsdb_destroy(old_db);
++
++                pa->elems[1] = ovsdb_to_txn_json(
++                                    new_db, "converted by ovsdb-tool");
++                ovsdb_destroy(new_db);
++
++                json_destroy(data_json);
++                data_json = pa->elems[1];
++            }
++
++            ovsdb_schema_destroy(schema);
++            check_ovsdb_error(ovsdb_log_reset(db_log_data));
++            check_ovsdb_error(ovsdb_log_write(db_log_data, schema_json));
++        }
+         if (data_json->type != JSON_NULL) {
+             check_ovsdb_error(ovsdb_log_write(db_log_data, data_json));
+         }
+@@ -1579,15 +1613,14 @@ do_check_cluster(struct ovs_cmdl_context *ctx)
      }
      free(c.servers);
  
@@ -61218,6 +61463,34 @@ index d4a9e34cc4..df2e373c3c 100644
          hmap_remove(&c.leaders, &leader->hmap_node);
          free(leader);
      }
+@@ -1636,7 +1669,8 @@ do_compare_versions(struct ovs_cmdl_context *ctx)
+ }
+ 
+ static void
+-do_convert_to_standalone(struct ovsdb_log *log, struct ovsdb_log *db_log_data)
++do_convert_to_standalone(const char *db_file_name,
++                         struct ovsdb_log *log, struct ovsdb_log *db_log_data)
+ {
+     for (unsigned int i = 0; ; i++) {
+         struct json *json;
+@@ -1653,7 +1687,7 @@ do_convert_to_standalone(struct ovsdb_log *log, struct ovsdb_log *db_log_data)
+         } else {
+             struct raft_record r;
+             check_ovsdb_error(raft_record_from_json(&r, json));
+-            raft_record_to_standalone_log(&r, db_log_data);
++            raft_record_to_standalone_log(db_file_name, &r, db_log_data);
+             raft_record_uninit(&r);
+         }
+         json_destroy(json);
+@@ -1676,7 +1710,7 @@ do_cluster_standalone(struct ovs_cmdl_context *ctx)
+     if (strcmp(ovsdb_log_get_magic(log), RAFT_MAGIC) != 0) {
+         ovs_fatal(0, "Database is not clustered db.\n");
+     }
+-    do_convert_to_standalone(log, db_log_data);
++    do_convert_to_standalone(db_file_name, log, db_log_data);
+     check_ovsdb_error(ovsdb_log_commit_block(db_log_data));
+     ovsdb_log_close(db_log_data);
+     ovsdb_log_close(log);
 diff --git a/ovsdb/ovsdb.c b/ovsdb/ovsdb.c
 index e6d866182c..3eb6ce870a 100644
 --- a/ovsdb/ovsdb.c
@@ -61676,7 +61949,7 @@ index 1a3447a8dd..cf9edf35c6 100644
          failure_test = FT_NO_TEST;
          unixctl_command_reply(conn, "test dismissed");
 diff --git a/ovsdb/relay.c b/ovsdb/relay.c
-index ef0e44d340..122ee8c52f 100644
+index ef0e44d340..423b98ee7c 100644
 --- a/ovsdb/relay.c
 +++ b/ovsdb/relay.c
 @@ -269,9 +269,9 @@ ovsdb_relay_clear(struct ovsdb *db)
@@ -61691,6 +61964,65 @@ index ef0e44d340..122ee8c52f 100644
              ovsdb_txn_row_delete(txn, row);
          }
      }
+@@ -283,6 +283,8 @@ static void
+ ovsdb_relay_parse_update(struct relay_ctx *ctx,
+                          const struct ovsdb_cs_update_event *update)
+ {
++    struct ovsdb_error *error = NULL;
++
+     if (!ctx->db) {
+         return;
+     }
+@@ -290,15 +292,27 @@ ovsdb_relay_parse_update(struct relay_ctx *ctx,
+     if (update->monitor_reply && ctx->new_schema) {
+         /* There was a schema change.  Updating a database with a new schema
+          * before processing monitor reply with the new data. */
+-        ctx->schema_change_cb(ctx->db, ctx->new_schema,
+-                              ctx->schema_change_aux);
++        error = ctx->schema_change_cb(ctx->db, ctx->new_schema, false,
++                                      ctx->schema_change_aux);
++        if (error) {
++            /* Should never happen, but handle this case anyway. */
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++            char *s = ovsdb_error_to_string_free(error);
++
++            VLOG_ERR_RL(&rl, "%s", s);
++            free(s);
++
++            ovsdb_cs_flag_inconsistency(ctx->cs);
++            return;
++        }
+         ovsdb_schema_destroy(ctx->new_schema);
+         ctx->new_schema = NULL;
+     }
+ 
+     struct ovsdb_cs_db_update *du;
+-    struct ovsdb_error *error = ovsdb_cs_parse_db_update(update->table_updates,
+-                                                         update->version, &du);
++
++    error = ovsdb_cs_parse_db_update(update->table_updates,
++                                     update->version, &du);
+     if (!error) {
+         if (update->clear) {
+             error = ovsdb_relay_clear(ctx->db);
+diff --git a/ovsdb/relay.h b/ovsdb/relay.h
+index 390ea70c82..2d66b5e5fa 100644
+--- a/ovsdb/relay.h
++++ b/ovsdb/relay.h
+@@ -23,8 +23,11 @@ struct json;
+ struct ovsdb;
+ struct ovsdb_schema;
+ 
+-typedef void (*schema_change_callback)(struct ovsdb *,
+-                                       const struct ovsdb_schema *, void *aux);
++typedef struct ovsdb_error *(*schema_change_callback)(
++                                       struct ovsdb *,
++                                       const struct ovsdb_schema *,
++                                       bool conversion_with_no_data,
++                                       void *aux);
+ 
+ void ovsdb_relay_add_db(struct ovsdb *, const char *remote,
+                         schema_change_callback schema_change_cb,
 diff --git a/ovsdb/replication.c b/ovsdb/replication.c
 index d8b56d8131..477c69d701 100644
 --- a/ovsdb/replication.c
@@ -61718,6 +62050,61 @@ index d8b56d8131..477c69d701 100644
          hmap_remove(&replication_dbs->map, &node->node);
          struct replication_db *rdb = node->data;
          if (rdb->active_db_schema) {
+diff --git a/ovsdb/storage.c b/ovsdb/storage.c
+index d4984be250..6069c4f102 100644
+--- a/ovsdb/storage.c
++++ b/ovsdb/storage.c
+@@ -622,7 +622,7 @@ ovsdb_storage_store_snapshot(struct ovsdb_storage *storage,
+ 
+ struct ovsdb_write * OVS_WARN_UNUSED_RESULT
+ ovsdb_storage_write_schema_change(struct ovsdb_storage *storage,
+-                                  const struct json *schema,
++                                  const struct ovsdb_schema *schema,
+                                   const struct json *data,
+                                   const struct uuid *prereq,
+                                   struct uuid *resultp)
+@@ -632,13 +632,22 @@ ovsdb_storage_write_schema_change(struct ovsdb_storage *storage,
+     if (storage->error) {
+         w->error = ovsdb_error_clone(storage->error);
+     } else if (storage->raft) {
+-        struct json *txn_json = json_array_create_2(json_clone(schema),
+-                                                    json_clone(data));
+-        w->command = raft_command_execute(storage->raft, txn_json,
+-                                          prereq, &result);
+-        json_destroy(txn_json);
++        /* Clustered storage doesn't support ephemeral columns. */
++        w->error = ovsdb_schema_check_for_ephemeral_columns(schema);
++        if (!w->error) {
++            struct json *schema_json, *txn_json;
++
++            schema_json = ovsdb_schema_to_json(schema);
++            txn_json = json_array_create_2(schema_json, json_clone(data));
++            w->command = raft_command_execute(storage->raft, txn_json,
++                                              prereq, &result);
++            json_destroy(txn_json);
++        }
+     } else if (storage->log) {
+-        w->error = ovsdb_storage_store_snapshot__(storage, schema, data);
++        struct json *schema_json = ovsdb_schema_to_json(schema);
++
++        w->error = ovsdb_storage_store_snapshot__(storage, schema_json, data);
++        json_destroy(schema_json);
+     } else {
+         /* When 'error' and 'command' are both null, it indicates that the
+          * command is complete.  This is fine since this unbacked storage drops
+diff --git a/ovsdb/storage.h b/ovsdb/storage.h
+index ff026b77fa..6c69e53134 100644
+--- a/ovsdb/storage.h
++++ b/ovsdb/storage.h
+@@ -84,7 +84,7 @@ struct ovsdb_error *ovsdb_storage_store_snapshot(struct ovsdb_storage *storage,
+ 
+ struct ovsdb_write *ovsdb_storage_write_schema_change(
+     struct ovsdb_storage *,
+-    const struct json *schema, const struct json *data,
++    const struct ovsdb_schema *, const struct json *data,
+     const struct uuid *prereq, struct uuid *result)
+     OVS_WARN_UNUSED_RESULT;
+ 
 diff --git a/ovsdb/table.c b/ovsdb/table.c
 index 455a3663fe..2184701ec1 100644
 --- a/ovsdb/table.c
@@ -61774,7 +62161,7 @@ index d15f2f1d6d..963e937957 100644
      }
  }
 diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
-index db86d847c3..9adbabf808 100644
+index db86d847c3..8eafefa4bf 100644
 --- a/ovsdb/transaction.c
 +++ b/ovsdb/transaction.c
 @@ -159,15 +159,15 @@ ovsdb_txn_row_abort(struct ovsdb_txn *txn OVS_UNUSED,
@@ -62008,6 +62395,15 @@ index db86d847c3..9adbabf808 100644
              if (r->old) {
                  ovsdb_row_destroy(r->old);
              }
+@@ -1189,7 +1191,7 @@ ovsdb_txn_precheck_prereq(const struct ovsdb *db)
+ 
+ struct ovsdb_txn_progress *
+ ovsdb_txn_propose_schema_change(struct ovsdb *db,
+-                                const struct json *schema,
++                                const struct ovsdb_schema *schema,
+                                 const struct json *data)
+ {
+     struct ovsdb_txn_progress *progress = xzalloc(sizeof *progress);
 @@ -1549,19 +1551,19 @@ for_each_txn_row(struct ovsdb_txn *txn,
      serial++;
  
@@ -62043,8 +62439,29 @@ index db86d847c3..9adbabf808 100644
          ovs_list_remove(&txn_h_node->node);
          ovsdb_txn_destroy_cloned(txn_h_node->txn);
          free(txn_h_node);
+diff --git a/ovsdb/transaction.h b/ovsdb/transaction.h
+index 6b5bb7f24b..9991f34d24 100644
+--- a/ovsdb/transaction.h
++++ b/ovsdb/transaction.h
+@@ -21,6 +21,7 @@
+ 
+ struct json;
+ struct ovsdb;
++struct ovsdb_schema;
+ struct ovsdb_table;
+ struct uuid;
+ 
+@@ -41,7 +42,7 @@ struct ovsdb_error *ovsdb_txn_propose_commit_block(struct ovsdb_txn *,
+ void ovsdb_txn_complete(struct ovsdb_txn *);
+ 
+ struct ovsdb_txn_progress *ovsdb_txn_propose_schema_change(
+-    struct ovsdb *, const struct json *schema, const struct json *data);
++    struct ovsdb *, const struct ovsdb_schema *, const struct json *data);
+ 
+ bool ovsdb_txn_progress_is_complete(const struct ovsdb_txn_progress *);
+ const struct ovsdb_error *ovsdb_txn_progress_get_error(
 diff --git a/ovsdb/trigger.c b/ovsdb/trigger.c
-index 726c138bf0..7d3003bca3 100644
+index 726c138bf0..3a693855b9 100644
 --- a/ovsdb/trigger.c
 +++ b/ovsdb/trigger.c
 @@ -146,14 +146,14 @@ ovsdb_trigger_prereplace_db(struct ovsdb_trigger *trigger)
@@ -62064,6 +62481,26 @@ index 726c138bf0..7d3003bca3 100644
          if (run_triggers
              || now - t->created >= t->timeout_msec
              || t->progress || t->txn_forward) {
+@@ -274,8 +274,8 @@ ovsdb_trigger_try(struct ovsdb_trigger *t, long long int now)
+             if (!error) {
+                 error = ovsdb_convert(t->db, new_schema, &newdb);
+             }
+-            ovsdb_schema_destroy(new_schema);
+             if (error) {
++                ovsdb_schema_destroy(new_schema);
+                 trigger_convert_error(t, error);
+                 return false;
+             }
+@@ -286,7 +286,8 @@ ovsdb_trigger_try(struct ovsdb_trigger *t, long long int now)
+ 
+             /* Propose the change. */
+             t->progress = ovsdb_txn_propose_schema_change(
+-                t->db, new_schema_json, txn_json);
++                t->db, new_schema, txn_json);
++            ovsdb_schema_destroy(new_schema);
+             json_destroy(txn_json);
+             t->reply = jsonrpc_create_reply(json_object_create(),
+                                             t->request->id);
 diff --git a/python/ovs/_json.c b/python/ovs/_json.c
 index ef7bb4b8ee..c36a140a8e 100644
 --- a/python/ovs/_json.c
@@ -66861,6 +67298,90 @@ index 876cb836cd..4a183bf186 100644
              --remote=ptcp:0:127.0.0.1                             dnl
              db.tmp], [0], [stdout], [stderr])
  PARSE_LISTENING_PORT([listener.log], [BAD_TCP_PORT])
+diff --git a/tests/ovsdb-tool.at b/tests/ovsdb-tool.at
+index 12ad6fb3fc..5496ccda77 100644
+--- a/tests/ovsdb-tool.at
++++ b/tests/ovsdb-tool.at
+@@ -465,6 +465,7 @@ AT_SETUP([ovsdb-tool convert-to-standalone])
+ AT_KEYWORDS([ovsdb file positive])
+ ordinal_schema > schema
+ AT_CHECK([ovsdb-tool create-cluster db schema unix:s1.raft], [0], [stdout], [ignore])
++on_exit 'kill `cat ovsdb-server.pid`'
+ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket --log-file db >/dev/null 2>&1])
+ for txn in m4_foreach([txn], [[[["ordinals",
+       {"op": "insert",
+@@ -498,3 +499,71 @@ OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+ # Make sure both standalone and cluster db data matches.
+ AT_CHECK([diff standalonedump clusterdump])
+ AT_CLEANUP
++
++AT_SETUP([ovsdb-tool convert-to-standalone after schema conversion])
++AT_KEYWORDS([ovsdb file positive])
++ordinal_schema > schema
++AT_CHECK([ovsdb-tool create-cluster db schema unix:s1.raft], [0], [stdout], [ignore])
++on_exit 'kill `cat ovsdb-server.pid`'
++AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --remote=punix:socket dnl
++                       --log-file db >/dev/null 2>&1])
++for txn in m4_foreach([txn], [[[["ordinals",
++      {"op": "insert",
++       "table": "ordinals",
++       "row": {"number": 0, "name": "zero"}},
++      {"op": "insert",
++       "table": "ordinals",
++       "row": {"number": 1, "name": "one"}},
++      {"op": "insert",
++       "table": "ordinals",
++       "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do
++  AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore])
++done
++
++dnl Change the schema.
++AT_CHECK([sed 's/5\.1\.3/5.1.4/' < schema > schema2])
++AT_CHECK([sed -i'back' -e '/.*"number":.*/a \
++           "is_seven": {"type": "boolean"},
++           ' schema2])
++
++dnl Convert the database.
++AT_CHECK([ovsdb-client convert unix:socket schema2])
++
++dnl Add a new row with a new column.
++AT_CHECK([ovsdb-client transact unix:socket dnl
++  '[["ordinals",
++     {"op": "insert",
++      "table": "ordinals",
++      "row": {"number": 7, "name": "seven", "is_seven": true}
++     }]]'], [0], [ignore], [ignore])
++
++AT_CHECK([ovsdb-client dump unix:socket > clusterdump])
++
++AT_CHECK([uuidfilt clusterdump], [0], [dnl
++ordinals table
++_uuid                                is_seven name  number
++------------------------------------ -------- ----- ------
++<0> false    one   1
++<1> false    two   2
++<2> false    zero  0
++<3> true     seven 7
++])
++
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++dnl Convert to standalone database from clustered database.
++AT_CHECK(ovsdb-tool cluster-to-standalone db1 db)
++
++dnl Check it's a standalone db.
++AT_CHECK([ovsdb-tool db-is-standalone db1])
++
++dnl Dump the standalone db data.
++AT_CHECK([ovsdb-server -vconsole:off -vfile -vvlog:off --detach --no-chdir dnl
++                       --pidfile --log-file --remote=punix:db.sock db1])
++AT_CHECK([ovsdb_client_wait ordinals connected])
++AT_CHECK([ovsdb-client dump > standalonedump])
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++dnl Make sure both standalone and cluster db data matches.
++AT_CHECK([diff standalonedump clusterdump])
++AT_CLEANUP
 diff --git a/tests/packet-type-aware.at b/tests/packet-type-aware.at
 index 054dcc9ccf..d63528e69e 100644
 --- a/tests/packet-type-aware.at
@@ -67521,7 +68042,7 @@ index d21fd777dd..c37852b216 100644
  
  OVS_VSWITCHD_STOP
 diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at
-index 80bc1dd5c3..84bab88be2 100644
+index 80bc1dd5c3..bf60e4cb27 100644
 --- a/tests/system-offloads-traffic.at
 +++ b/tests/system-offloads-traffic.at
 @@ -90,7 +90,7 @@ AT_CHECK([tc -o -s -d filter show dev ovs-p0 ingress |
@@ -67542,7 +68063,7 @@ index 80bc1dd5c3..84bab88be2 100644
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
  
-@@ -168,3 +168,131 @@ matchall
+@@ -168,3 +168,186 @@ matchall
  ])
  OVS_TRAFFIC_VSWITCHD_STOP
  AT_CLEANUP
@@ -67674,6 +68195,61 @@ index 80bc1dd5c3..84bab88be2 100644
 +
 +OVS_TRAFFIC_VSWITCHD_STOP
 +AT_CLEANUP
++
++AT_SETUP([offloads - delete ufid mapping if device not exist - offloads enabled])
++OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true])
++
++AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"])
++
++ADD_NAMESPACES(at_ns0, at_ns1, at_ns2)
++
++dnl Disable IPv6 to skip unexpected flow
++AT_CHECK([sysctl -w net.ipv6.conf.br0.disable_ipv6=1], [0], [ignore])
++NS_CHECK_EXEC([at_ns0], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore])
++NS_CHECK_EXEC([at_ns1], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore])
++NS_CHECK_EXEC([at_ns2], [sysctl -w net.ipv6.conf.all.disable_ipv6=1], [0], [ignore])
++
++ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24", "aa:1a:54:e9:c5:56")
++ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
++
++NS_CHECK_EXEC([at_ns0], [ping -q -c 2 -i 0.2 10.1.1.2 | FORMAT_PING], [0], [dnl
++2 packets transmitted, 2 received, 0% packet loss, time 0ms
++])
++
++dnl Delete and add interface ovs-p0/p0
++AT_CHECK([ip link del dev ovs-p0])
++AT_CHECK([ip link add p0 type veth peer name ovs-p0 || return 77])
++AT_CHECK([ip link set p0 netns at_ns0])
++AT_CHECK([ip link set dev ovs-p0 up])
++NS_CHECK_EXEC([at_ns0], [ip addr add dev p0 "10.1.1.1/24"])
++NS_CHECK_EXEC([at_ns0], [ip link set dev p0 up])
++NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address "aa:1a:54:e9:c5:56"])
++
++AT_CHECK([ovs-appctl revalidator/purge], [0])
++
++dnl Generate flows to trigger the hmap expand once
++ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24")
++NS_CHECK_EXEC([at_ns0], [ping -q -c 2 -i 0.2 10.1.1.2 | FORMAT_PING], [0], [dnl
++2 packets transmitted, 2 received, 0% packet loss, time 0ms
++])
++NS_CHECK_EXEC([at_ns0], [ping -q -c 2 -i 0.2 10.1.1.3 | FORMAT_PING], [0], [dnl
++2 packets transmitted, 2 received, 0% packet loss, time 0ms
++])
++
++AT_CHECK([ovs-appctl revalidator/purge], [0])
++dnl Fix purge fail occasionally
++AT_CHECK([ovs-appctl revalidator/purge], [0])
++
++AT_CHECK([test $(ovs-appctl dpctl/dump-flows | grep -c "eth_type(0x0800)") -eq 0], [0], [ignore])
++
++OVS_TRAFFIC_VSWITCHD_STOP(["/could not open network device ovs-p0/d
++/on nonexistent port/d
++/failed to flow_get/d
++/Failed to acquire udpif_key/d
++/No such device/d
++/failed to offload flow/d
++"])
++AT_CLEANUP
 diff --git a/tests/system-route.at b/tests/system-route.at
 index 1714273e35..270956d13f 100644
 --- a/tests/system-route.at
@@ -70301,7 +70877,7 @@ index 08957bdf46..779ea60aee 100755
  $FUNCNAME: attach the default sandbox to an interconnection network
  usage: $FUNCNAME NETWORK BRIDGE
 diff --git a/utilities/ovs-tcpdump.in b/utilities/ovs-tcpdump.in
-index 82d1bedfa6..a49ec9f942 100755
+index 82d1bedfa6..420c11eb8a 100755
 --- a/utilities/ovs-tcpdump.in
 +++ b/utilities/ovs-tcpdump.in
 @@ -44,6 +44,7 @@ try:
@@ -70392,7 +70968,21 @@ index 82d1bedfa6..a49ec9f942 100755
      pipes = _doexec(*([dump_cmd, '-i', mirror_interface] + tcpdargs))
      try:
          while pipes.poll() is None:
-@@ -512,17 +541,6 @@ def main():
+@@ -509,20 +538,20 @@ def main():
+             print(data.decode('utf-8'))
+         raise KeyboardInterrupt
+     except KeyboardInterrupt:
++        # If there is a pipe behind ovs-tcpdump (such as ovs-tcpdump
++        # -i eth0 | grep "192.168.1.1"), the pipe is no longer available
++        # after received Ctrl+C.
++        # If we write data to an unavailable pipe, a pipe error will be
++        # reported, so we turn off stdout to avoid subsequent flushing
++        # of data into the pipe.
++        try:
++            sys.stdout.close()
++        except IOError:
++            pass
++
          if pipes.poll() is None:
              pipes.terminate()
  
diff --git a/SPECS/openvswitch2.17.spec b/SPECS/openvswitch2.17.spec
index 26e2dc4..fb1740a 100644
--- a/SPECS/openvswitch2.17.spec
+++ b/SPECS/openvswitch2.17.spec
@@ -57,7 +57,7 @@ Summary: Open vSwitch
 Group: System Environment/Daemons daemon/database/utilities
 URL: http://www.openvswitch.org/
 Version: 2.17.0
-Release: 77%{?dist}
+Release: 82%{?dist}
 
 # Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
 # lib/sflow*.[ch] files are SISSL
@@ -748,6 +748,40 @@ exit 0
 %endif
 
 %changelog
+* Mon Apr 24 2023 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-82
+- Merging upstream branch-2.17 [RH git: 34bbf561db]
+    Commit list:
+    b082241945 ovsdb: Allow conversion records with no data in a clustered storage.
+    efcdf6c0de ovsdb: Check for ephemeral columns before writing a new schema.
+    bf39ea3c79 ovsdb-tool: Fix cluster-to-standalone for DB conversion records.
+
+
+* Fri Apr 07 2023 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-81
+- Merging upstream branch-2.17 [RH git: a1438ec598]
+    Commit list:
+    4f82f89039 ovs-tcpdump: Stdout is shutdown before ovs-tcpdump exit.
+
+
+* Thu Apr 06 2023 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-80
+- Merging upstream branch-2.17 [RH git: a34e49c430]
+    Commit list:
+    77116d9903 Prepare for 2.17.7.
+    a08bb41e3c Set release date for 2.17.6.
+
+
+* Thu Apr 06 2023 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-79
+- Merging upstream branch-2.17 [RH git: 6a73129db8]
+    Commit list:
+    27fb5db7f7 ofproto-dpif-xlate: Always mask ip proto field. (#2134873)
+
+
+* Tue Apr 04 2023 Open vSwitch CI <ovs-ci@redhat.com> - 2.17.0-78
+- Merging upstream branch-2.17 [RH git: dd004f4cf6]
+    Commit list:
+    c3684a0604 conntrack-tp: Fix clang warning.
+    be19308aaf netdev-offload-tc: Del ufid mapping if device not exist.
+
+
 * Tue Apr 04 2023 Timothy Redaelli <tredaelli@redhat.com> - 2.17.0-77
 - ofproto-dpif-xlate: Always mask ip proto field. [RH git: cfd5c61966] (#2134873)
     The ofproto layer currently treats nw_proto field as overloaded to mean