diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5651078
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+SOURCES/*.tar.gz
+SRPMS
diff --git a/.ovn.metadata b/.ovn.metadata
new file mode 100644
index 0000000..c06f182
--- /dev/null
+++ b/.ovn.metadata
@@ -0,0 +1,5 @@
+002450621b33c5690060345b0aac25bc2426d675  SOURCES/docutils-0.12.tar.gz
+3de4c32ea0b8076955ee1c5fee5cf33d08208598  SOURCES/openvswitch-c598c05.tar.gz
+ed85aed0acfee4eca42a63b0a979a141525ebdc9  SOURCES/ovn-24.09.0.tar.gz
+d34f96421a86004aa5d26ecf975edefd09f948b1  SOURCES/Pygments-1.4.tar.gz
+6beb30f18ffac3de7689b7fd63e9a8a7d9c8df3a  SOURCES/Sphinx-1.1.3.tar.gz
diff --git a/SOURCES/arm64-armv8a-linuxapp-gcc-config b/SOURCES/arm64-armv8a-linuxapp-gcc-config
new file mode 100644
index 0000000..5813d7a
--- /dev/null
+++ b/SOURCES/arm64-armv8a-linuxapp-gcc-config
@@ -0,0 +1,540 @@
+# -*- cfg-sha: 9fc8b53ccd53cc8b64391f6252e1dba558ae660a73a72f10dcadff2ca5462243
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2015 Cavium, Inc
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017 Cavium, Inc
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2016 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2017 Intel Corporation
+# RTE_EXEC_ENV values are the directories in mk/exec-env/
+CONFIG_RTE_EXEC_ENV="linuxapp"
+# RTE_ARCH values are architecture we compile for. directories in mk/arch/
+CONFIG_RTE_ARCH="arm64"
+# machine can define specific variables or action for a specific board
+# RTE_MACHINE values are architecture we compile for. directories in mk/machine/
+CONFIG_RTE_MACHINE="armv8a"
+# The compiler we use.
+# RTE_TOOLCHAIN values are architecture we compile for. directories in mk/toolchain/
+CONFIG_RTE_TOOLCHAIN="gcc"
+# Use intrinsics or assembly code for key routines
+CONFIG_RTE_FORCE_INTRINSICS=y
+# Machine forces strict alignment constraints.
+CONFIG_RTE_ARCH_STRICT_ALIGN=n
+# Compile to share library
+CONFIG_RTE_BUILD_SHARED_LIB=n
+# Use newest code breaking previous ABI
+CONFIG_RTE_NEXT_ABI=n
+# Major ABI to overwrite library specific LIBABIVER
+CONFIG_RTE_MAJOR_ABI=
+# Machine's cache line size
+CONFIG_RTE_CACHE_LINE_SIZE=128
+# Memory model
+CONFIG_RTE_USE_C11_MEM_MODEL=y
+# Compile Environment Abstraction Layer
+CONFIG_RTE_LIBRTE_EAL=y
+CONFIG_RTE_MAX_LCORE=256
+CONFIG_RTE_MAX_NUMA_NODES=8
+CONFIG_RTE_MAX_HEAPS=32
+CONFIG_RTE_MAX_MEMSEG_LISTS=64
+# each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages
+# or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_LIST=8192
+CONFIG_RTE_MAX_MEM_MB_PER_LIST=32768
+# a "type" is a combination of page size and NUMA node. total number of memseg
+# lists per type will be limited to either RTE_MAX_MEMSEG_PER_TYPE pages (split
+# over multiple lists of RTE_MAX_MEMSEG_PER_LIST pages), or
+# RTE_MAX_MEM_MB_PER_TYPE megabytes of memory (split over multiple lists of
+# RTE_MAX_MEM_MB_PER_LIST), whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_TYPE=32768
+CONFIG_RTE_MAX_MEM_MB_PER_TYPE=131072
+# global maximum usable amount of VA, in megabytes
+CONFIG_RTE_MAX_MEM_MB=524288
+CONFIG_RTE_MAX_MEMZONE=2560
+CONFIG_RTE_MAX_TAILQ=32
+CONFIG_RTE_ENABLE_ASSERT=n
+CONFIG_RTE_LOG_DP_LEVEL=RTE_LOG_INFO
+CONFIG_RTE_LOG_HISTORY=256
+CONFIG_RTE_BACKTRACE=y
+CONFIG_RTE_LIBEAL_USE_HPET=n
+CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
+CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_EAL_IGB_UIO=n
+CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MAX_VFIO_GROUPS=64
+CONFIG_RTE_MAX_VFIO_CONTAINERS=64
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y
+CONFIG_RTE_USE_LIBBSD=n
+# Recognize/ignore architecture we compile for. AVX/AVX512 CPU flags for performance/power testing.
+# AVX512 is marked as experimental for now, will enable it after enough
+# field test and possible optimization.
+CONFIG_RTE_ENABLE_AVX=y
+CONFIG_RTE_ENABLE_AVX512=n
+# Default driver path (or "" to disable)
+CONFIG_RTE_EAL_PMD_PATH=""
+# Compile Environment Abstraction Layer to support Vmware TSC map
+CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=y
+# Compile architecture we compile for. PCI library
+CONFIG_RTE_LIBRTE_PCI=y
+# Compile architecture we compile for. argument parser library
+CONFIG_RTE_LIBRTE_KVARGS=y
+# Compile generic ethernet library
+CONFIG_RTE_LIBRTE_ETHER=y
+CONFIG_RTE_LIBRTE_ETHDEV_DEBUG=n
+CONFIG_RTE_MAX_ETHPORTS=32
+CONFIG_RTE_MAX_QUEUES_PER_PORT=1024
+CONFIG_RTE_LIBRTE_IEEE1588=n
+CONFIG_RTE_ETHDEV_QUEUE_STAT_CNTRS=16
+CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y
+CONFIG_RTE_ETHDEV_PROFILE_WITH_VTUNE=n
+# Turn off Tx preparation stage
+# Warning: rte_eth_tx_prepare() can be safely disabled only if using a
+# driver which do not implement any Tx preparation.
+CONFIG_RTE_ETHDEV_TX_PREPARE_NOOP=n
+# Common libraries, before Bus/PMDs
+CONFIG_RTE_LIBRTE_COMMON_DPAAX=n
+# Compile architecture we compile for. Intel FPGA bus
+CONFIG_RTE_LIBRTE_IFPGA_BUS=n
+# Compile PCI bus driver
+CONFIG_RTE_LIBRTE_PCI_BUS=y
+# Compile architecture we compile for. vdev bus
+CONFIG_RTE_LIBRTE_VDEV_BUS=y
+# Compile ARK PMD
+CONFIG_RTE_LIBRTE_ARK_PMD=n
+CONFIG_RTE_LIBRTE_ARK_PAD_TX=y
+CONFIG_RTE_LIBRTE_ARK_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_STATS=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TRACE=n
+# Compile Aquantia Atlantic PMD driver
+CONFIG_RTE_LIBRTE_ATLANTIC_PMD=n
+# Compile AMD PMD
+CONFIG_RTE_LIBRTE_AXGBE_PMD=n
+CONFIG_RTE_LIBRTE_AXGBE_PMD_DEBUG=n
+# Compile burst-oriented Broadcom PMD driver
+CONFIG_RTE_LIBRTE_BNX2X_PMD=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_BNX2X_MF_SUPPORT=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_PERIODIC=n
+# Compile burst-oriented Broadcom BNXT PMD driver
+CONFIG_RTE_LIBRTE_BNXT_PMD=n
+# Compile burst-oriented Chelsio Terminator (CXGBE) PMD
+CONFIG_RTE_LIBRTE_CXGBE_PMD=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_REG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_CXGBE_TPUT=y
+# NXP DPAA Bus
+CONFIG_RTE_LIBRTE_DPAA_BUS=n
+CONFIG_RTE_LIBRTE_DPAA_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA_PMD=n
+CONFIG_RTE_LIBRTE_DPAA_HWDEBUG=n
+# Compile NXP DPAA2 FSL-MC Bus
+CONFIG_RTE_LIBRTE_FSLMC_BUS=n
+# Compile Support Libraries for NXP DPAA2
+CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA2_USE_PHYS_IOVA=y
+# Compile burst-oriented NXP DPAA2 PMD driver
+CONFIG_RTE_LIBRTE_DPAA2_PMD=n
+CONFIG_RTE_LIBRTE_DPAA2_DEBUG_DRIVER=n
+# Compile NXP ENETC PMD Driver
+CONFIG_RTE_LIBRTE_ENETC_PMD=n
+# Compile burst-oriented Amazon ENA PMD driver
+CONFIG_RTE_LIBRTE_ENA_PMD=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_ENA_COM_DEBUG=n
+# Compile burst-oriented Cisco ENIC PMD driver
+CONFIG_RTE_LIBRTE_ENIC_PMD=n
+# Compile burst-oriented IGB & EM PMD drivers
+CONFIG_RTE_LIBRTE_EM_PMD=n
+CONFIG_RTE_LIBRTE_IGB_PMD=y
+CONFIG_RTE_LIBRTE_E1000_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_E1000_PF_DISABLE_STRIP_CRC=n
+# Compile burst-oriented IXGBE PMD driver
+CONFIG_RTE_LIBRTE_IXGBE_PMD=y
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_IXGBE_PF_DISABLE_STRIP_CRC=n
+CONFIG_RTE_IXGBE_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_IXGBE_BYPASS=n
+# Compile burst-oriented I40E PMD driver
+CONFIG_RTE_LIBRTE_I40E_PMD=y
+CONFIG_RTE_LIBRTE_I40E_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_I40E_RX_ALLOW_BULK_ALLOC=y
+CONFIG_RTE_LIBRTE_I40E_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC=n
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_PF=64
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM=4
+# Compile burst-oriented FM10K PMD
+CONFIG_RTE_LIBRTE_FM10K_PMD=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_FM10K_RX_OLFLAGS_ENABLE=y
+CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR=y
+# Compile burst-oriented AVF PMD driver
+CONFIG_RTE_LIBRTE_AVF_PMD=n
+CONFIG_RTE_LIBRTE_AVF_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVF_16BYTE_RX_DESC=n
+# Compile burst-oriented Mellanox ConnectX-3 (MLX4) PMD
+CONFIG_RTE_LIBRTE_MLX4_PMD=n
+CONFIG_RTE_LIBRTE_MLX4_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=n
+# Compile burst-oriented Mellanox ConnectX-4, ConnectX-5 & Bluefield
+# (MLX5) PMD
+CONFIG_RTE_LIBRTE_MLX5_PMD=n
+CONFIG_RTE_LIBRTE_MLX5_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS=n
+# Compile burst-oriented Netronome NFP PMD driver
+CONFIG_RTE_LIBRTE_NFP_PMD=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_RX=n
+# QLogic 10G/25G/40G/50G/100G PMD
+CONFIG_RTE_LIBRTE_QEDE_PMD=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_RX=n
+#Provides abs path/name of architecture we compile for. firmware file.
+#Empty string denotes driver will use default firmware
+CONFIG_RTE_LIBRTE_QEDE_FW=""
+# Compile burst-oriented Solarflare libefx-based PMD
+CONFIG_RTE_LIBRTE_SFC_EFX_PMD=n
+CONFIG_RTE_LIBRTE_SFC_EFX_DEBUG=n
+# Compile software PMD backed by SZEDATA2 device
+CONFIG_RTE_LIBRTE_PMD_SZEDATA2=n
+# Compile burst-oriented Cavium Thunderx NICVF PMD driver
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_TX=n
+# Compile burst-oriented Cavium LiquidIO PMD driver
+CONFIG_RTE_LIBRTE_LIO_PMD=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_REGS=n
+# Compile burst-oriented Cavium OCTEONTX network PMD driver
+CONFIG_RTE_LIBRTE_OCTEONTX_PMD=n
+# Compile WRS accelerated virtual port (AVP) guest PMD driver
+CONFIG_RTE_LIBRTE_AVP_PMD=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_BUFFERS=n
+# Compile burst-oriented VIRTIO PMD driver
+CONFIG_RTE_LIBRTE_VIRTIO_PMD=y
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DUMP=n
+# Compile virtio device emulation inside virtio PMD driver
+CONFIG_RTE_VIRTIO_USER=n
+# Compile burst-oriented VMXNET3 PMD driver
+CONFIG_RTE_LIBRTE_VMXNET3_PMD=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX_FREE=n
+# Compile software PMD backed by AF_PACKET sockets (Linux only)
+CONFIG_RTE_LIBRTE_PMD_AF_PACKET=n
+# Compile link bonding PMD library
+CONFIG_RTE_LIBRTE_PMD_BOND=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB_L1=n
+# Compile fail-safe PMD
+CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y
+# Compile Marvell PMD driver
+CONFIG_RTE_LIBRTE_MVPP2_PMD=n
+# Compile Marvell MVNETA PMD driver
+CONFIG_RTE_LIBRTE_MVNETA_PMD=n
+# Compile support for VMBus library
+CONFIG_RTE_LIBRTE_VMBUS=n
+# Compile native PMD for Hyper-V/Azure
+CONFIG_RTE_LIBRTE_NETVSC_PMD=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_DUMP=n
+# Compile virtual device driver for NetVSC on Hyper-V/Azure
+CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=n
+# Compile null PMD
+CONFIG_RTE_LIBRTE_PMD_NULL=n
+# Compile software PMD backed by PCAP files
+CONFIG_RTE_LIBRTE_PMD_PCAP=n
+# Compile example software rings based PMD
+CONFIG_RTE_LIBRTE_PMD_RING=y
+CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16
+CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16
+# Compile SOFTNIC PMD
+CONFIG_RTE_LIBRTE_PMD_SOFTNIC=n
+# Compile architecture we compile for. TAP PMD
+# It is enabled by default for Linux only.
+CONFIG_RTE_LIBRTE_PMD_TAP=y
+# Do prefetch of packet data within PMD driver receive function
+CONFIG_RTE_PMD_PACKET_PREFETCH=y
+# Compile generic wireless base band device library
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_BBDEV=n
+CONFIG_RTE_BBDEV_MAX_DEVS=128
+CONFIG_RTE_BBDEV_OFFLOAD_COST=n
+# Compile PMD for NULL bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL=y
+# Compile PMD for turbo software bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_TURBO_SW=n
+# Compile generic crypto device library
+CONFIG_RTE_LIBRTE_CRYPTODEV=n
+CONFIG_RTE_CRYPTO_MAX_DEVS=64
+# Compile PMD for ARMv8 Crypto device
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO=n
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO_DEBUG=n
+# Compile NXP CAAM JR crypto Driver
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR=n
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE=n
+# Compile NXP DPAA2 crypto sec driver for CAAM HW
+CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC=n
+# NXP DPAA caam - crypto driver
+CONFIG_RTE_LIBRTE_PMD_DPAA_SEC=n
+CONFIG_RTE_LIBRTE_DPAA_MAX_CRYPTODEV=4
+# Compile PMD for Cavium OCTEON TX crypto device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_CRYPTO=y
+# Compile PMD for QuickAssist based devices - see docs for details
+CONFIG_RTE_LIBRTE_PMD_QAT=n
+CONFIG_RTE_LIBRTE_PMD_QAT_SYM=n
+# Max. number of QuickAssist devices, which can be detected and attached
+CONFIG_RTE_PMD_QAT_MAX_PCI_DEVICES=48
+CONFIG_RTE_PMD_QAT_COMP_SGL_MAX_SEGMENTS=16
+CONFIG_RTE_PMD_QAT_COMP_IM_BUFFER_SIZE=65536
+# Compile PMD for virtio crypto devices
+CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO=n
+# Number of maximum virtio crypto devices
+CONFIG_RTE_MAX_VIRTIO_CRYPTO=32
+# Compile PMD for AESNI backed device
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB=n
+# Compile PMD for Software backed device
+CONFIG_RTE_LIBRTE_PMD_OPENSSL=n
+# Compile PMD for AESNI GCM device
+CONFIG_RTE_LIBRTE_PMD_AESNI_GCM=n
+# Compile PMD for SNOW 3G device
+CONFIG_RTE_LIBRTE_PMD_SNOW3G=n
+CONFIG_RTE_LIBRTE_PMD_SNOW3G_DEBUG=n
+# Compile PMD for KASUMI device
+CONFIG_RTE_LIBRTE_PMD_KASUMI=n
+# Compile PMD for ZUC device
+CONFIG_RTE_LIBRTE_PMD_ZUC=n
+# Compile PMD for Crypto Scheduler device
+CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER=n
+# Compile PMD for NULL Crypto device
+CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO=n
+# Compile PMD for AMD CCP crypto device
+CONFIG_RTE_LIBRTE_PMD_CCP=n
+# Compile PMD for Marvell Crypto device
+CONFIG_RTE_LIBRTE_PMD_MVSAM_CRYPTO=n
+# Compile generic security library
+CONFIG_RTE_LIBRTE_SECURITY=n
+# Compile generic compression device library
+CONFIG_RTE_LIBRTE_COMPRESSDEV=n
+CONFIG_RTE_COMPRESS_MAX_DEVS=64
+# Compile compressdev unit test
+CONFIG_RTE_COMPRESSDEV_TEST=n
+# Compile PMD for Octeontx ZIPVF compression device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF=n
+# Compile PMD for ISA-L compression device
+CONFIG_RTE_LIBRTE_PMD_ISAL=n
+# Compile PMD for ZLIB compression device
+CONFIG_RTE_LIBRTE_PMD_ZLIB=n
+# Compile generic event device library
+CONFIG_RTE_LIBRTE_EVENTDEV=n
+CONFIG_RTE_LIBRTE_EVENTDEV_DEBUG=n
+CONFIG_RTE_EVENT_MAX_DEVS=16
+CONFIG_RTE_EVENT_MAX_QUEUES_PER_DEV=64
+CONFIG_RTE_EVENT_TIMER_ADAPTER_NUM_MAX=32
+CONFIG_RTE_EVENT_ETH_INTR_RING_SIZE=1024
+CONFIG_RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE=32
+CONFIG_RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE=32
+# Compile PMD for skeleton event device
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV=n
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV_DEBUG=n
+# Compile PMD for software event device
+CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV=n
+# Compile PMD for distributed software event device
+CONFIG_RTE_LIBRTE_PMD_DSW_EVENTDEV=n
+# Compile PMD for octeontx sso event device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF=n
+# Compile PMD for OPDL event device
+CONFIG_RTE_LIBRTE_PMD_OPDL_EVENTDEV=n
+# Compile PMD for NXP DPAA event device
+CONFIG_RTE_LIBRTE_PMD_DPAA_EVENTDEV=n
+# Compile PMD for NXP DPAA2 event device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_EVENTDEV=n
+# Compile raw device support
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_RAWDEV=n
+CONFIG_RTE_RAWDEV_MAX_DEVS=10
+CONFIG_RTE_LIBRTE_PMD_SKELETON_RAWDEV=n
+# Compile PMD for NXP DPAA2 CMDIF raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_CMDIF_RAWDEV=n
+# Compile PMD for NXP DPAA2 QDMA raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=n
+# Compile PMD for Intel FPGA raw device
+CONFIG_RTE_LIBRTE_PMD_IFPGA_RAWDEV=n
+# Compile librte_ring
+CONFIG_RTE_LIBRTE_RING=y
+# Compile librte_mempool
+CONFIG_RTE_LIBRTE_MEMPOOL=y
+CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=512
+CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n
+# Compile Mempool drivers
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET=y
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB=64
+CONFIG_RTE_DRIVER_MEMPOOL_RING=y
+CONFIG_RTE_DRIVER_MEMPOOL_STACK=y
+# Compile PMD for octeontx fpa mempool device
+CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL=n
+# Compile librte_mbuf
+CONFIG_RTE_LIBRTE_MBUF=y
+CONFIG_RTE_LIBRTE_MBUF_DEBUG=n
+CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc"
+CONFIG_RTE_MBUF_REFCNT_ATOMIC=y
+CONFIG_RTE_PKTMBUF_HEADROOM=128
+# Compile librte_timer
+CONFIG_RTE_LIBRTE_TIMER=n
+CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
+# Compile librte_cfgfile
+CONFIG_RTE_LIBRTE_CFGFILE=n
+# Compile librte_cmdline
+CONFIG_RTE_LIBRTE_CMDLINE=y
+CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n
+# Compile librte_hash
+CONFIG_RTE_LIBRTE_HASH=y
+CONFIG_RTE_LIBRTE_HASH_DEBUG=n
+# Compile librte_efd
+CONFIG_RTE_LIBRTE_EFD=n
+# Compile librte_member
+CONFIG_RTE_LIBRTE_MEMBER=y
+# Compile librte_jobstats
+CONFIG_RTE_LIBRTE_JOBSTATS=n
+# Compile architecture we compile for. device metrics library
+CONFIG_RTE_LIBRTE_METRICS=y
+# Compile architecture we compile for. bitrate statistics library
+CONFIG_RTE_LIBRTE_BITRATE=y
+# Compile architecture we compile for. latency statistics library
+CONFIG_RTE_LIBRTE_LATENCY_STATS=y
+# Compile librte_telemetry
+CONFIG_RTE_LIBRTE_TELEMETRY=n
+# Compile librte_lpm
+CONFIG_RTE_LIBRTE_LPM=n
+CONFIG_RTE_LIBRTE_LPM_DEBUG=n
+# Compile librte_acl
+CONFIG_RTE_LIBRTE_ACL=n
+CONFIG_RTE_LIBRTE_ACL_DEBUG=n
+# Compile librte_power
+CONFIG_RTE_LIBRTE_POWER=n
+CONFIG_RTE_LIBRTE_POWER_DEBUG=n
+CONFIG_RTE_MAX_LCORE_FREQS=64
+# Compile librte_net
+CONFIG_RTE_LIBRTE_NET=y
+# Compile librte_ip_frag
+CONFIG_RTE_LIBRTE_IP_FRAG=y
+CONFIG_RTE_LIBRTE_IP_FRAG_DEBUG=n
+CONFIG_RTE_LIBRTE_IP_FRAG_MAX_FRAG=4
+CONFIG_RTE_LIBRTE_IP_FRAG_TBL_STAT=n
+# Compile GRO library
+CONFIG_RTE_LIBRTE_GRO=y
+# Compile GSO library
+CONFIG_RTE_LIBRTE_GSO=y
+# Compile librte_meter
+CONFIG_RTE_LIBRTE_METER=y
+# Compile librte_classify
+CONFIG_RTE_LIBRTE_FLOW_CLASSIFY=n
+# Compile librte_sched
+CONFIG_RTE_LIBRTE_SCHED=n
+CONFIG_RTE_SCHED_DEBUG=n
+CONFIG_RTE_SCHED_RED=n
+CONFIG_RTE_SCHED_COLLECT_STATS=n
+CONFIG_RTE_SCHED_SUBPORT_TC_OV=n
+CONFIG_RTE_SCHED_PORT_N_GRINDERS=8
+CONFIG_RTE_SCHED_VECTOR=n
+# Compile architecture we compile for. distributor library
+CONFIG_RTE_LIBRTE_DISTRIBUTOR=n
+# Compile architecture we compile for. reorder library
+CONFIG_RTE_LIBRTE_REORDER=n
+# Compile librte_port
+CONFIG_RTE_LIBRTE_PORT=n
+CONFIG_RTE_PORT_STATS_COLLECT=n
+CONFIG_RTE_PORT_PCAP=n
+# Compile librte_table
+CONFIG_RTE_LIBRTE_TABLE=n
+CONFIG_RTE_TABLE_STATS_COLLECT=n
+# Compile librte_pipeline
+CONFIG_RTE_LIBRTE_PIPELINE=n
+CONFIG_RTE_PIPELINE_STATS_COLLECT=n
+# Compile librte_kni
+CONFIG_RTE_LIBRTE_KNI=n
+CONFIG_RTE_LIBRTE_PMD_KNI=n
+CONFIG_RTE_KNI_KMOD=n
+CONFIG_RTE_KNI_KMOD_ETHTOOL=n
+CONFIG_RTE_KNI_PREEMPT_DEFAULT=y
+# Compile architecture we compile for. pdump library
+CONFIG_RTE_LIBRTE_PDUMP=y
+# Compile vhost user library
+CONFIG_RTE_LIBRTE_VHOST=y
+CONFIG_RTE_LIBRTE_VHOST_NUMA=y
+CONFIG_RTE_LIBRTE_VHOST_DEBUG=n
+# Compile vhost PMD
+# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled.
+CONFIG_RTE_LIBRTE_PMD_VHOST=y
+# Compile IFC driver
+# To compile, CONFIG_RTE_LIBRTE_VHOST and CONFIG_RTE_EAL_VFIO
+# should be enabled.
+CONFIG_RTE_LIBRTE_IFC_PMD=n
+# Compile librte_bpf
+CONFIG_RTE_LIBRTE_BPF=n
+# allow load BPF from ELF files (requires libelf)
+CONFIG_RTE_LIBRTE_BPF_ELF=n
+# Compile architecture we compile for. test application
+CONFIG_RTE_APP_TEST=y
+CONFIG_RTE_APP_TEST_RESOURCE_TAR=n
+# Compile architecture we compile for. procinfo application
+CONFIG_RTE_PROC_INFO=y
+# Compile architecture we compile for. PMD test application
+CONFIG_RTE_TEST_PMD=n
+CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n
+CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n
+# Compile architecture we compile for. bbdev test application
+CONFIG_RTE_TEST_BBDEV=n
+# Compile architecture we compile for. crypto performance application
+CONFIG_RTE_APP_CRYPTO_PERF=n
+# Compile architecture we compile for. eventdev application
+CONFIG_RTE_APP_EVENTDEV=n
+CONFIG_RTE_EXEC_ENV_LINUXAPP=y
+CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n
+# Common libraries, before Bus/PMDs
+# NXP DPAA BUS and drivers
+# NXP FSLMC BUS and DPAA2 drivers
+# NXP ENETC PMD Driver
+CONFIG_RTE_ARCH_ARM64=y
+CONFIG_RTE_ARCH_64=y
+# Maximum available cache line size in arm64 implementations.
+# Setting to maximum available cache line size in generic config
+# to address minimum DMA alignment across all arm64 implementations.
+# Accelarate rte_memcpy. Be sure to run unit test (memcpy_perf_autotest)
+# to determine architecture we compile for. best threshold in code. Refer to notes in source file
+# (lib/librte_eal/common/include/arch/arm/rte_memcpy_64.h) for more info.
+CONFIG_RTE_ARCH_ARM64_MEMCPY=n
+#CONFIG_RTE_ARM64_MEMCPY_ALIGNED_THRESHOLD=2048
+#CONFIG_RTE_ARM64_MEMCPY_UNALIGNED_THRESHOLD=512
+# Leave below RTE_ARM64_MEMCPY_xxx options commented out, unless there're
+# strong reasons.
+#CONFIG_RTE_ARM64_MEMCPY_SKIP_GCC_VER_CHECK=n
+#CONFIG_RTE_ARM64_MEMCPY_ALIGN_MASK=0xF
+#CONFIG_RTE_ARM64_MEMCPY_STRICT_ALIGN=n
+CONFIG_RTE_TOOLCHAIN_GCC=y
+CONFIG_RTE_LIBRTE_PMD_XENVIRT=n
diff --git a/SOURCES/configlib.sh b/SOURCES/configlib.sh
new file mode 100644
index 0000000..a1049b3
--- /dev/null
+++ b/SOURCES/configlib.sh
@@ -0,0 +1,105 @@
+# Copyright (C) 2017, Red Hat, Inc.
+#
+# Core configuration file library.
+
+# Configurations are determined by sha values.  The way to determine is by
+# the special text:
+# $FILE_COMMENT_TYPE -*- cfg-sha: $SHA256 -*-
+
+export LC_ALL=C
+
+# check required binaries
+__check_reqd_binaries() {
+    local BIN __binaries=("egrep" "sort" "sha256sum" "sed")
+    for BIN in $__binaries; do
+        if ! type -P $BIN >/dev/null 2>&1; then
+            echo "Binary $BIN not found.  Please install."
+            exit 1
+        fi
+    done
+}
+
+# Calculates a sha from a file
+# The algorithm for generating a sha from a config is thus:
+#
+# 1. Remove all comment lines and blank lines
+# 2. Sort the content
+# 3. generate the sha-256 sum
+#
+# From a script perspective, this means:
+#   egrep -v ^\# %file% | egrep -v ^$ | sort -u | sha256sum
+#
+# Params:
+#  $1 = output variable
+#  $2 = file to use to calculate the shasum
+#  $3 = file comment type (defaults to # if unspecified)
+calc_sha() {
+    __check_reqd_binaries
+
+    if [ "$1" == "" ]; then
+        echo "Please pass in a storage variable."
+        return 1
+    fi
+
+    local __resultvar=$1
+    __retval=1
+    shift
+
+    local __file=$1
+    local cmnt=${2:-#}
+
+    if [ -f "$__file" ]; then
+        local __shasum=$(egrep -v ^"$cmnt" "$__file" | egrep -v ^$ | sort -u | sha256sum -t | cut -d" " -f1)
+        eval $__resultvar="'$__shasum'"
+        __retval=0
+    fi
+    return $__retval
+}
+
+# Retrieves a sha stored in a file
+# Param:
+#  $1 = output variable
+#  $2 = file to use to calculate the shasum
+#  $3 = file comment type (defaults to # if unspecified)
+retr_sha() {
+    __check_reqd_binaries
+
+    if [ "$1" == "" ]; then
+        echo "Please pass in a storage variable."
+        return 1
+    fi
+
+    local __resultvar=$1
+    __retval=1
+    shift
+
+    local __file=$1
+    local cmnt=${2:-#}
+
+    if [ -f "$__file" ]; then
+        if grep -q "$cmnt -\*- cfg-sha:" "$__file"; then
+            local __shasum=$(grep "$cmnt -\*- cfg-sha:" "$__file" | sed -e "s@$cmnt -\*- cfg-sha: @@" | cut -d" " -f1)
+            eval $__resultvar="'$__shasum'"
+            __retval=0
+        fi
+    fi
+    return $__retval
+}
+
+
+# Set a config value
+# set_conf dpdk_build_tree parameter value
+# dpdk_build_tree is the directory where the .config lives
+# parameter is the config parameter
+# value is the value to set for the config parameter
+set_conf() {
+    c="$1/.config"
+    shift
+
+    if grep -q "$1" "$c"; then
+        sed -i "s:^$1=.*$:$1=$2:g" $c
+    else
+        echo $1=$2 >> "$c"
+    fi
+}
+
diff --git a/SOURCES/gen_config_group.sh b/SOURCES/gen_config_group.sh
new file mode 100755
index 0000000..d1c06fe
--- /dev/null
+++ b/SOURCES/gen_config_group.sh
@@ -0,0 +1,216 @@
+#!/bin/bash
+
+source configlib.sh
+
+# Generates arch configurations in the current directory based on
+# 1. an openvswitch.spec file
+# 2. an expanded dpdk tree
+
+if (( $# != 2 )); then
+    echo "$0: openvswitch.spec dpdk_tree" >&2
+    exit 1
+fi
+
+OVSSPEC="$1"
+DPDKDIR="$2"
+
+# accumulate all arch + name triples
+OVS_DPDK_CONF_MACH_ARCH=()
+for arch in $(grep %define\ dpdk_mach_arch "$OVSSPEC" | sed 's@%define dpdk_mach_arch @@')
+do
+    OVS_DPDK_CONF_MACH_ARCH+=($arch)
+done
+
+OVS_DPDK_CONF_MACH_TMPL=()
+for tmpl in $(grep %define\ dpdk_mach_tmpl "$OVSSPEC" | sed 's@%define dpdk_mach_tmpl @@')
+do
+    OVS_DPDK_CONF_MACH_TMPL+=($tmpl)
+done
+
+OVS_DPDK_CONF_MACH=()
+for mach in $(grep %define\ dpdk_mach\  "$OVSSPEC" | sed 's@%define dpdk_mach @@')
+do
+    OVS_DPDK_CONF_MACH+=($mach)
+done
+
+OVS_DPDK_TARGETS=()
+for ((i=0; i < ${#OVS_DPDK_CONF_MACH[@]}; i++));
+do
+    OVS_DPDK_TARGETS+=("${OVS_DPDK_CONF_MACH_ARCH[$i]}-${OVS_DPDK_CONF_MACH_TMPL[$i]}-linuxapp-gcc")
+    echo "DPDK-target: ${OVS_DPDK_TARGETS[$i]}"
+done
+
+OUTPUT_DIR=$(pwd)
+pushd "$DPDKDIR"
+for ((i=0; i < ${#OVS_DPDK_TARGETS[@]}; i++));
+do
+    echo "For ${OVS_DPDK_TARGETS[$i]}:"
+
+    echo "     a. Generating initial config"
+    echo "        make V=1 T=${OVS_DPDK_TARGETS[$i]} O=${OVS_DPDK_TARGETS[$i]}"
+    make V=1 T=${OVS_DPDK_TARGETS[$i]} O=${OVS_DPDK_TARGETS[$i]} -j8 config
+    ORIG_SHA=""
+    OUTDIR="${OVS_DPDK_TARGETS[$i]}"
+
+    echo "     b. calculating and applying sha"
+    calc_sha ORIG_SHA "${OUTDIR}/.config"
+    if [ "$ORIG_SHA" == "" ]; then
+        echo "ERROR: Unable to get sha for arch ${OVS_DPDK_TARGETS[$i]}"
+        exit 1
+    fi
+    echo "# -*- cfg-sha: ${ORIG_SHA}" > ${OUTDIR}/.config.new
+    cat "${OUTDIR}/.config" >> "${OUTDIR}/.config.new"
+    cp "${OUTDIR}/.config" "${OUTDIR}/.config.orig"
+    mv -f "${OUTDIR}/.config.new" "${OUTDIR}/.config"
+
+    echo "     c. setting initial configurations"
+    # these are the original setconf values from openvswitch.spec
+    set_conf "${OUTDIR}" CONFIG_RTE_MACHINE "\\\"${OVS_DPDK_CONF_MACH[$i]}\\\""
+
+    # Disable DPDK libraries not needed
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_TIMER n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_CFGFILE n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_JOBSTATS n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_LPM n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_ACL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_POWER n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_SCHED n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_DISTRIBUTOR n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_REORDER n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PORT n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_TABLE n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PIPELINE n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_KNI n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_CRYPTODEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_SECURITY n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_FLOW_CLASSIFY n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_BBDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_COMPRESSDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_BPF n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_DPAA_MEMPOOL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_CFGFILE n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_EFD n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_FLOW_CLASSIFY n
+
+    # Disable all eventdevs
+    for eventdev in $(grep _EVENTDEV= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g')
+    do
+        set_conf "${OUTDIR}" $eventdev n
+    done
+
+    # Disable all rawdevs
+    for rawdev in $(grep _RAWDEV= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g')
+    do
+        set_conf "${OUTDIR}" $rawdev n
+    done
+
+    # Disable virtio user
+    set_conf "${OUTDIR}" CONFIG_RTE_VIRTIO_USER n
+
+    # Enable vhost numa as libnuma dep is ok
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VHOST_NUMA y
+
+    # start by disabling ALL PMDs
+    for pmd in $(grep _PMD= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g')
+    do
+        set_conf "${OUTDIR}" $pmd n
+    done
+
+    # PMDs which have their own naming scheme
+    # the default for this was 'n' at one point.  Make sure we keep it
+    # as such
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_QAT n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_VHOST n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_KNI n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_XENVIRT n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_NULL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_PCAP n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_BOND n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_AF_PACKET n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_SOFTNIC n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_DPAA_SEC n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_COMMON_DPAAX n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_CAAM_JR n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE n
+
+    # whitelist of enabled PMDs
+    # Soft PMDs to enable
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_RING y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_VHOST y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VIRTIO_PMD y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_TAP y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_FAILSAFE y
+
+
+    # start by disabling all buses
+    for bus in $(grep _BUS= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g')
+    do
+        set_conf "${OUTDIR}" $bus n
+    done
+
+    # blacklist buses that don't conform to std naming
+    # May override VMBUS later in arch specific section
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VMBUS n
+
+    # whitelist buses
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PCI_BUS y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VDEV_BUS y
+
+
+    # Disable some other miscellanous items related to test apps
+    set_conf "${OUTDIR}" CONFIG_RTE_TEST_BBDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_APP_CRYPTO_PERF n
+
+    # Disable kernel modules
+    set_conf "${OUTDIR}" CONFIG_RTE_EAL_IGB_UIO n
+    set_conf "${OUTDIR}" CONFIG_RTE_KNI_KMOD n
+
+    # Disable experimental stuff
+    set_conf "${OUTDIR}" CONFIG_RTE_NEXT_ABI n
+
+    # Arch specific
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_I40E_PMD y
+    case "${OVS_DPDK_CONF_MACH_ARCH[i]}" in
+    x86_64)
+        # Hw PMD
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_BNXT_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_ENIC_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX4_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX5_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_NFP_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_QEDE_PMD y
+        # Sw PMD
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_NETVSC_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD y
+        # Bus
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VMBUS y
+        ;&
+    arm64)
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_IXGBE_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_IGB_PMD y
+        ;;
+    esac
+
+    cp "${OUTDIR}/.config" "${OUTPUT_DIR}/${OVS_DPDK_TARGETS[$i]}-config"
+done
+popd >/dev/null
+
+printf "For each arch ( "
+for ((i=0; i < ${#OVS_DPDK_CONF_MACH_ARCH[@]}; i++));
+do
+    printf "${OVS_DPDK_CONF_MACH_ARCH[i]} "
+done
+echo "):"
+echo "1. ensure you enable the requisite hw"
diff --git a/SOURCES/ovn24.09.patch b/SOURCES/ovn24.09.patch
new file mode 100644
index 0000000..e047421
--- /dev/null
+++ b/SOURCES/ovn24.09.patch
@@ -0,0 +1,3877 @@
+diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml
+index 9c062971e..bfef352a7 100644
+--- a/.github/workflows/containers.yml
++++ b/.github/workflows/containers.yml
+@@ -15,7 +15,7 @@ env:
+ 
+ jobs:
+   container:
+-    if: github.repository_owner == env.IMAGE_NAMESPACE
++    if: github.repository_owner == 'ovn-org'
+     runs-on: ubuntu-24.04
+     strategy:
+       matrix:
+diff --git a/.github/workflows/ovn-fake-multinode-tests.yml b/.github/workflows/ovn-fake-multinode-tests.yml
+index 795dafc22..f3f25ddf3 100644
+--- a/.github/workflows/ovn-fake-multinode-tests.yml
++++ b/.github/workflows/ovn-fake-multinode-tests.yml
+@@ -72,7 +72,7 @@ jobs:
+ 
+   multinode-tests:
+     runs-on: ubuntu-22.04
+-    timeout-minutes: 15
++    timeout-minutes: 30
+     needs: [build]
+     strategy:
+       fail-fast: false
+diff --git a/Documentation/automake.mk b/Documentation/automake.mk
+index c6cc37e49..5f7500fb7 100644
+--- a/Documentation/automake.mk
++++ b/Documentation/automake.mk
+@@ -56,6 +56,7 @@ DOC_SOURCE = \
+ 	Documentation/internals/security.rst \
+ 	Documentation/internals/contributing/index.rst \
+ 	Documentation/internals/contributing/backporting-patches.rst \
++	Documentation/internals/contributing/inclusive-language.rst \
+ 	Documentation/internals/contributing/coding-style.rst \
+ 	Documentation/internals/contributing/documentation-style.rst \
+ 	Documentation/internals/contributing/submitting-patches.rst \
+diff --git a/Documentation/index.rst b/Documentation/index.rst
+index 04e757505..9fb298c28 100644
+--- a/Documentation/index.rst
++++ b/Documentation/index.rst
+@@ -81,6 +81,7 @@ Learn more about the Open Virtual Network (OVN) project and about how you can co
+ 
+ - **Contributing:** :doc:`internals/contributing/submitting-patches` |
+   :doc:`internals/contributing/backporting-patches` |
++  :doc:`internals/contributing/inclusive-language` |
+   :doc:`internals/contributing/coding-style`
+ 
+ - **Maintaining:** :doc:`internals/maintainers` |
+diff --git a/Documentation/internals/contributing/inclusive-language.rst b/Documentation/internals/contributing/inclusive-language.rst
+new file mode 100644
+index 000000000..65e9c4fbd
+--- /dev/null
++++ b/Documentation/internals/contributing/inclusive-language.rst
+@@ -0,0 +1,57 @@
++..
++      Licensed under the Apache License, Version 2.0 (the "License"); you may
++      not use this file except in compliance with the License. You may obtain
++      a copy of the License at
++
++          http://www.apache.org/licenses/LICENSE-2.0
++
++      Unless required by applicable law or agreed to in writing, software
++      distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
++      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
++      License for the specific language governing permissions and limitations
++      under the License.
++
++      Convention for heading levels in OVN documentation:
++
++      =======  Heading 0 (reserved for the title in a document)
++      -------  Heading 1
++      ~~~~~~~  Heading 2
++      +++++++  Heading 3
++      '''''''  Heading 4
++
++      Avoid deeper levels because they do not render well.
++
++==================
++Inclusive Language
++==================
++
++In order to help facilitate an inclusive environment in the OVN
++community we recognise the role of language in framing our
++communication with each other. It is important that terms that
++may exclude people through racial, cultural or other bias, are avoided
++as they may make people feel excluded.
++
++We recognise that this is subjective, and to some extent is a journey.
++But we also recognise that we cannot begin that journey without taking
++positive action. To this end OVN is adopting the practice of an
++inclusive word list, which helps to guide the use of language within
++the project.
++
++.. _word list:
++
++Word List
++---------
++
++The intent of this document is to formally document the acceptance of a
++inclusive word list by OVN.  Accordingly, this document specifies
++use of the use the `Inclusive Naming Word List
++<https://inclusivenaming.org/word-lists/>`__ v1.0 (the word list) for
++OVN.
++
++The adoption of the word list intended that this act as a guide for
++developers creating patches to the OVN repository, including both
++source code and documentation. And to aid maintainers in their role of
++shepherding changes into the repository.
++
++Further steps to align usage of language in OVN, including clarification
++of application of the word list, to new and existing work, may follow.
+diff --git a/Documentation/internals/contributing/index.rst b/Documentation/internals/contributing/index.rst
+index ba6b6094e..9dab48110 100644
+--- a/Documentation/internals/contributing/index.rst
++++ b/Documentation/internals/contributing/index.rst
+@@ -31,6 +31,7 @@ The below guides provide information on contributing to OVN itself.
+    :maxdepth: 2
+ 
+    submitting-patches
++   inclusive-language
+    backporting-patches
+    coding-style
+    documentation-style
+diff --git a/NEWS b/NEWS
+index d8717810a..201d5d40f 100644
+--- a/NEWS
++++ b/NEWS
+@@ -1,4 +1,7 @@
+-OVN v24.09.0 - xx xxx xxxx
++OVN v24.09.1 - xx xxx xxxx
++--------------------------
++
++OVN v24.09.0 - 13 Sep 2024
+ --------------------------
+   - Added a new logical switch port option "pkt_clone_type".
+     If the value is set to "mc_unknown", packets destined to the port gets
+@@ -56,7 +59,7 @@ OVN v24.09.0 - xx xxx xxxx
+   - The NB_Global.debug_drop_domain_id configured value is now overridden by
+     the ID associated with the Sampling_App record created for drop sampling
+     (Sampling_App.type configured as "drop").
+-    - Add support for ACL sampling through the new Sample_Collector and Sample
++  - Add support for ACL sampling through the new Sample_Collector and Sample
+     tables.  Sampling is supported for both traffic that creates new
+     connections and for traffic that is part of an existing connection.
+   - Add "external_ids:ovn-encap-ip-default" config for ovn-controller to
+@@ -64,6 +67,10 @@ OVN v24.09.0 - xx xxx xxxx
+     configured.
+   - Added a new column in the southbound database "flow_desc" to provide
+     human readable context to flows.
++  - Added two new experimental logical router port options,
++    "routing-protocol-redirect" and "routing-protocols", that allow
++    redirection of routing protocol traffic received by a router port
++    to a different logical switch port.
+ 
+ OVN v24.03.0 - 01 Mar 2024
+ --------------------------
+diff --git a/configure.ac b/configure.ac
+index cd6a5d0c9..53c834faa 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -13,7 +13,7 @@
+ # limitations under the License.
+ 
+ AC_PREREQ(2.63)
+-AC_INIT(ovn, 24.09.0, bugs@openvswitch.org)
++AC_INIT(ovn, 24.09.1, bugs@openvswitch.org)
+ AC_CONFIG_MACRO_DIR([m4])
+ AC_CONFIG_AUX_DIR([build-aux])
+ AC_CONFIG_HEADERS([config.h])
+diff --git a/controller/chassis.c b/controller/chassis.c
+index 2991a0af3..ee839084a 100644
+--- a/controller/chassis.c
++++ b/controller/chassis.c
+@@ -390,6 +390,7 @@ chassis_build_other_config(const struct ovs_chassis_cfg *ovs_cfg,
+     smap_replace(config, OVN_FEATURE_CT_COMMIT_TO_ZONE, "true");
+     smap_replace(config, OVN_FEATURE_SAMPLE_WITH_REGISTERS,
+                  ovs_cfg->sample_with_regs ? "true" : "false");
++    smap_replace(config, OVN_FEATURE_CT_NEXT_ZONE, "true");
+ }
+ 
+ /*
+@@ -549,6 +550,12 @@ chassis_other_config_changed(const struct ovs_chassis_cfg *ovs_cfg,
+         return true;
+     }
+ 
++    if (!smap_get_bool(&chassis_rec->other_config,
++                       OVN_FEATURE_CT_NEXT_ZONE,
++                       false)) {
++        return true;
++    }
++
+     return false;
+ }
+ 
+@@ -706,6 +713,7 @@ update_supported_sset(struct sset *supported)
+     sset_add(supported, OVN_FEATURE_CT_COMMIT_NAT_V2);
+     sset_add(supported, OVN_FEATURE_CT_COMMIT_TO_ZONE);
+     sset_add(supported, OVN_FEATURE_SAMPLE_WITH_REGISTERS);
++    sset_add(supported, OVN_FEATURE_CT_NEXT_ZONE);
+ }
+ 
+ static void
+diff --git a/controller/ct-zone.c b/controller/ct-zone.c
+index 77eb16ac9..469a8fc54 100644
+--- a/controller/ct-zone.c
++++ b/controller/ct-zone.c
+@@ -216,12 +216,15 @@ ct_zones_update(const struct sset *local_lports,
+     struct shash_node *node;
+     SHASH_FOR_EACH_SAFE (node, &ctx->current) {
+         struct ct_zone *ct_zone = node->data;
+-        if (!sset_contains(&all_users, node->name) ||
+-            ct_zone->zone < min_ct_zone || ct_zone->zone > max_ct_zone) {
++        if (!sset_contains(&all_users, node->name)) {
+             ct_zone_remove(ctx, node->name);
+         } else if (!simap_find(&req_snat_zones, node->name)) {
+-            bitmap_set1(unreq_snat_zones_map, ct_zone->zone);
+-            simap_put(&unreq_snat_zones, node->name, ct_zone->zone);
++            if (ct_zone->zone < min_ct_zone || ct_zone->zone > max_ct_zone) {
++                ct_zone_remove(ctx, node->name);
++            } else {
++                bitmap_set1(unreq_snat_zones_map, ct_zone->zone);
++                simap_put(&unreq_snat_zones, node->name, ct_zone->zone);
++            }
+         }
+     }
+ 
+@@ -249,10 +252,11 @@ ct_zones_update(const struct sset *local_lports,
+ 
+         struct ct_zone *ct_zone = shash_find_data(&ctx->current,
+                                                   snat_req_node->name);
++        bool flush = !(ct_zone && ct_zone->zone == snat_req_node->data);
+         if (ct_zone && ct_zone->zone != snat_req_node->data) {
+             ct_zone_remove(ctx, snat_req_node->name);
+         }
+-        ct_zone_add(ctx, snat_req_node->name, snat_req_node->data, true);
++        ct_zone_add(ctx, snat_req_node->name, snat_req_node->data, flush);
+     }
+ 
+     /* xxx This is wasteful to assign a zone to each port--even if no
+diff --git a/controller/if-status.c b/controller/if-status.c
+index 9a7488057..ada78a18b 100644
+--- a/controller/if-status.c
++++ b/controller/if-status.c
+@@ -219,7 +219,8 @@ ovs_iface_create(struct if_status_mgr *, const char *iface_id,
+ static void add_to_ovn_uninstall_hash(struct if_status_mgr *, const char *,
+                                       const struct uuid *);
+ static void ovs_iface_destroy(struct if_status_mgr *, struct ovs_iface *);
+-static void ovn_uninstall_hash_destroy(struct if_status_mgr *mgr, char *name);
++static void ovn_uninstall_hash_destroy(struct if_status_mgr *mgr,
++                                       struct shash_node *node);
+ static void ovs_iface_set_state(struct if_status_mgr *, struct ovs_iface *,
+                                 enum if_state);
+ 
+@@ -256,7 +257,7 @@ if_status_mgr_clear(struct if_status_mgr *mgr)
+     ovs_assert(shash_is_empty(&mgr->ifaces));
+ 
+     SHASH_FOR_EACH_SAFE (node, &mgr->ovn_uninstall_hash) {
+-        ovn_uninstall_hash_destroy(mgr, node->data);
++        ovn_uninstall_hash_destroy(mgr, node);
+     }
+     ovs_assert(shash_is_empty(&mgr->ovn_uninstall_hash));
+ 
+@@ -789,20 +790,13 @@ ovs_iface_destroy(struct if_status_mgr *mgr, struct ovs_iface *iface)
+ }
+ 
+ static void
+-ovn_uninstall_hash_destroy(struct if_status_mgr *mgr, char *name)
++ovn_uninstall_hash_destroy(struct if_status_mgr *mgr, struct shash_node *node)
+ {
+-    struct shash_node *node = shash_find(&mgr->ovn_uninstall_hash, name);
+-    char *node_name = NULL;
+-    if (node) {
+-        free(node->data);
+-        VLOG_DBG("Interface name %s destroy", name);
+-        node_name = shash_steal(&mgr->ovn_uninstall_hash, node);
+-        ovn_uninstall_hash_account_mem(name, true);
+-        free(node_name);
+-    } else {
+-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+-        VLOG_WARN_RL(&rl, "Interface name %s not found", name);
+-    }
++    free(node->data);
++    VLOG_DBG("Interface name %s destroy", node->name);
++    char *node_name = shash_steal(&mgr->ovn_uninstall_hash, node);
++    ovn_uninstall_hash_account_mem(node_name, true);
++    free(node_name);
+ }
+ 
+ static void
+diff --git a/controller/ofctrl.c b/controller/ofctrl.c
+index e023cab9b..f9387d375 100644
+--- a/controller/ofctrl.c
++++ b/controller/ofctrl.c
+@@ -45,7 +45,6 @@
+ #include "ovn/actions.h"
+ #include "lib/extend-table.h"
+ #include "lib/lb.h"
+-#include "lib/ovn-util.h"
+ #include "openvswitch/poll-loop.h"
+ #include "physical.h"
+ #include "openvswitch/rconn.h"
+@@ -390,16 +389,9 @@ struct meter_band_entry {
+ 
+ static struct shash meter_bands;
+ 
+-static unsigned long *ecmp_nexthop_ids;
+-
+ static void ofctrl_meter_bands_destroy(void);
+ static void ofctrl_meter_bands_clear(void);
+ 
+-static void ecmp_nexthop_monitor_run(
+-        const struct sbrec_ecmp_nexthop_table *enh_table,
+-        struct ovs_list *msgs);
+-
+-
+ /* MFF_* field ID for our Geneve option.  In S_TLV_TABLE_MOD_SENT, this is
+  * the option we requested (we don't know whether we obtained it yet).  In
+  * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */
+@@ -438,7 +430,6 @@ ofctrl_init(struct ovn_extend_table *group_table,
+     groups = group_table;
+     meters = meter_table;
+     shash_init(&meter_bands);
+-    ecmp_nexthop_ids = bitmap_allocate(ECMP_NEXTHOP_IDS_LEN);
+ }
+ 
+ /* S_NEW, for a new connection.
+@@ -886,7 +877,6 @@ ofctrl_destroy(void)
+     expr_symtab_destroy(&symtab);
+     shash_destroy(&symtab);
+     ofctrl_meter_bands_destroy();
+-    bitmap_free(ecmp_nexthop_ids);
+ }
+ 
+ uint64_t
+@@ -2316,47 +2306,6 @@ add_meter(struct ovn_extend_table_info *m_desired,
+     ofctrl_meter_bands_alloc(sb_meter, m_desired, msgs);
+ }
+ 
+-static void
+-ecmp_nexthop_monitor_flush_ct_entry(uint64_t id, struct ovs_list *msgs)
+-{
+-    ovs_u128 mask = {
+-        /* ct_labels.label BITS[96-127] */
+-        .u64.hi = 0xffffffff00000000,
+-    };
+-    ovs_u128 nexthop = {
+-        .u64.hi = id << 32,
+-    };
+-    struct ofp_ct_match match = {
+-        .labels = nexthop,
+-        .labels_mask = mask,
+-    };
+-    struct ofpbuf *msg = ofp_ct_match_encode(&match, NULL,
+-                                             rconn_get_version(swconn));
+-    ovs_list_push_back(msgs, &msg->list_node);
+-}
+-
+-static void
+-ecmp_nexthop_monitor_run(const struct sbrec_ecmp_nexthop_table *enh_table,
+-                         struct ovs_list *msgs)
+-{
+-    unsigned long *ids = bitmap_allocate(ECMP_NEXTHOP_IDS_LEN);
+-
+-    const struct sbrec_ecmp_nexthop *sbrec_ecmp_nexthop;
+-    SBREC_ECMP_NEXTHOP_TABLE_FOR_EACH (sbrec_ecmp_nexthop, enh_table) {
+-        bitmap_set1(ids, sbrec_ecmp_nexthop->id);
+-    }
+-
+-    int id;
+-    BITMAP_FOR_EACH_1 (id, ECMP_NEXTHOP_IDS_LEN, ecmp_nexthop_ids) {
+-        if (!bitmap_is_set(ids, id)) {
+-            ecmp_nexthop_monitor_flush_ct_entry(id, msgs);
+-        }
+-    }
+-
+-    bitmap_free(ecmp_nexthop_ids);
+-    ecmp_nexthop_ids = ids;
+-}
+-
+ static void
+ installed_flow_add(struct ovn_flow *d,
+                    struct ofputil_bundle_ctrl_msg *bc,
+@@ -2715,7 +2664,6 @@ ofctrl_put(struct ovn_desired_flow_table *lflow_table,
+            struct shash *pending_ct_zones,
+            struct hmap *pending_lb_tuples,
+            struct ovsdb_idl_index *sbrec_meter_by_name,
+-           const struct sbrec_ecmp_nexthop_table *enh_table,
+            uint64_t req_cfg,
+            bool lflows_changed,
+            bool pflows_changed)
+@@ -2756,8 +2704,6 @@ ofctrl_put(struct ovn_desired_flow_table *lflow_table,
+     /* OpenFlow messages to send to the switch to bring it up-to-date. */
+     struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs);
+ 
+-    ecmp_nexthop_monitor_run(enh_table, &msgs);
+-
+     /* Iterate through ct zones that need to be flushed. */
+     struct shash_node *iter;
+     SHASH_FOR_EACH(iter, pending_ct_zones) {
+diff --git a/controller/ofctrl.h b/controller/ofctrl.h
+index 33953a8a4..129e3b6ad 100644
+--- a/controller/ofctrl.h
++++ b/controller/ofctrl.h
+@@ -31,7 +31,6 @@ struct ofpbuf;
+ struct ovsrec_bridge;
+ struct ovsrec_open_vswitch_table;
+ struct sbrec_meter_table;
+-struct sbrec_ecmp_nexthop_table;
+ struct shash;
+ 
+ struct ovn_desired_flow_table {
+@@ -60,7 +59,6 @@ void ofctrl_put(struct ovn_desired_flow_table *lflow_table,
+                 struct shash *pending_ct_zones,
+                 struct hmap *pending_lb_tuples,
+                 struct ovsdb_idl_index *sbrec_meter_by_name,
+-                const struct sbrec_ecmp_nexthop_table *enh_table,
+                 uint64_t nb_cfg,
+                 bool lflow_changed,
+                 bool pflow_changed);
+diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
+index 27a4996a8..3b2a0d6bb 100644
+--- a/controller/ovn-controller.c
++++ b/controller/ovn-controller.c
+@@ -4456,22 +4456,7 @@ pflow_output_if_status_mgr_handler(struct engine_node *node,
+         }
+         if (pb->n_additional_chassis) {
+             /* Update flows for all ports in datapath. */
+-            struct sbrec_port_binding *target =
+-                sbrec_port_binding_index_init_row(
+-                    p_ctx.sbrec_port_binding_by_datapath);
+-            sbrec_port_binding_index_set_datapath(target, pb->datapath);
+-
+-            const struct sbrec_port_binding *binding;
+-            SBREC_PORT_BINDING_FOR_EACH_EQUAL (
+-                    binding, target, p_ctx.sbrec_port_binding_by_datapath) {
+-                bool removed = sbrec_port_binding_is_deleted(binding);
+-                if (!physical_handle_flows_for_lport(binding, removed, &p_ctx,
+-                                                     &pfo->flow_table)) {
+-                    destroy_physical_ctx(&p_ctx);
+-                    return false;
+-                }
+-            }
+-            sbrec_port_binding_index_destroy_row(target);
++            physical_multichassis_reprocess(pb, &p_ctx, &pfo->flow_table);
+         } else {
+             /* If any multichassis ports, update flows for the port. */
+             bool removed = sbrec_port_binding_is_deleted(pb);
+@@ -5501,17 +5486,14 @@ main(int argc, char *argv[])
+                                            br_int_remote.probe_interval)) {
+                 VLOG_INFO("OVS feature set changed, force recompute.");
+                 engine_set_force_recompute(true);
+-                if (ovs_feature_set_discovered()) {
+-                    uint32_t max_groups = ovs_feature_max_select_groups_get();
+-                    uint32_t max_meters = ovs_feature_max_meters_get();
+-                    struct ed_type_lflow_output *lflow_out_data =
+-                        engine_get_internal_data(&en_lflow_output);
+-
+-                    ovn_extend_table_reinit(&lflow_out_data->group_table,
+-                                            max_groups);
+-                    ovn_extend_table_reinit(&lflow_out_data->meter_table,
+-                                            max_meters);
+-                }
++
++                struct ed_type_lflow_output *lflow_out_data =
++                    engine_get_internal_data(&en_lflow_output);
++
++                ovn_extend_table_reinit(&lflow_out_data->group_table,
++                                        ovs_feature_max_select_groups_get());
++                ovn_extend_table_reinit(&lflow_out_data->meter_table,
++                                        ovs_feature_max_meters_get());
+             }
+ 
+             if (br_int) {
+@@ -5725,8 +5707,6 @@ main(int argc, char *argv[])
+                                    &ct_zones_data->ctx.pending,
+                                    &lb_data->removed_tuples,
+                                    sbrec_meter_by_name,
+-                                   sbrec_ecmp_nexthop_table_get(
+-                                        ovnsb_idl_loop.idl),
+                                    ofctrl_seqno_get_req_cfg(),
+                                    engine_node_changed(&en_lflow_output),
+                                    engine_node_changed(&en_pflow_output));
+diff --git a/controller/physical.c b/controller/physical.c
+index 9e04ad5f2..c6db4f376 100644
+--- a/controller/physical.c
++++ b/controller/physical.c
+@@ -1258,6 +1258,12 @@ reply_imcp_error_if_pkt_too_big(struct ovn_desired_flow_table *flow_table,
+     ofpact_put_set_field(
+         &inner_ofpacts, mf_from_id(MFF_LOG_FLAGS), &value, &mask);
+ 
++    /* inport <-> outport */
++    put_stack(MFF_LOG_INPORT, ofpact_put_STACK_PUSH(&inner_ofpacts));
++    put_stack(MFF_LOG_OUTPORT, ofpact_put_STACK_PUSH(&inner_ofpacts));
++    put_stack(MFF_LOG_INPORT, ofpact_put_STACK_POP(&inner_ofpacts));
++    put_stack(MFF_LOG_OUTPORT, ofpact_put_STACK_POP(&inner_ofpacts));
++
+     /* eth.src <-> eth.dst */
+     put_stack(MFF_ETH_DST, ofpact_put_STACK_PUSH(&inner_ofpacts));
+     put_stack(MFF_ETH_SRC, ofpact_put_STACK_PUSH(&inner_ofpacts));
+@@ -1658,7 +1664,8 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                 sbrec_port_binding_by_name, binding->parent_port);
+ 
+             if (parent_port
+-                && !lport_can_bind_on_this_chassis(chassis, parent_port)) {
++                && (lport_can_bind_on_this_chassis(chassis,
++                    parent_port) != CAN_BIND_AS_MAIN)) {
+                 /* Even though there is an ofport for this container
+                  * parent port, it is requested on different chassis ignore
+                  * this container port.
+@@ -2397,33 +2404,9 @@ physical_handle_flows_for_lport(const struct sbrec_port_binding *pb,
+         }
+     }
+ 
+-    if (ldp) {
+-        bool multichassis_state_changed = (
+-            !!pb->additional_chassis ==
+-            !!shash_find(&ldp->multichassis_ports, pb->logical_port)
+-        );
+-        if (multichassis_state_changed) {
+-            if (pb->additional_chassis) {
+-                add_local_datapath_multichassis_port(
+-                    ldp, pb->logical_port, pb);
+-            } else {
+-                remove_local_datapath_multichassis_port(
+-                    ldp, pb->logical_port);
+-            }
+-
+-            struct sbrec_port_binding *target =
+-                sbrec_port_binding_index_init_row(
+-                    p_ctx->sbrec_port_binding_by_datapath);
+-            sbrec_port_binding_index_set_datapath(target, ldp->datapath);
+-
+-            const struct sbrec_port_binding *port;
+-            SBREC_PORT_BINDING_FOR_EACH_EQUAL (
+-                    port, target, p_ctx->sbrec_port_binding_by_datapath) {
+-                ofctrl_remove_flows(flow_table, &port->header_.uuid);
+-                physical_eval_port_binding(p_ctx, port, flow_table);
+-            }
+-            sbrec_port_binding_index_destroy_row(target);
+-        }
++    if (sbrec_port_binding_is_updated(
++            pb, SBREC_PORT_BINDING_COL_ADDITIONAL_CHASSIS) || removed) {
++        physical_multichassis_reprocess(pb, p_ctx, flow_table);
+     }
+ 
+     if (!removed) {
+@@ -2440,6 +2423,25 @@ physical_handle_flows_for_lport(const struct sbrec_port_binding *pb,
+     return true;
+ }
+ 
++void
++physical_multichassis_reprocess(const struct sbrec_port_binding *pb,
++                                struct physical_ctx *p_ctx,
++                                struct ovn_desired_flow_table *flow_table)
++{
++    struct sbrec_port_binding *target =
++            sbrec_port_binding_index_init_row(
++                    p_ctx->sbrec_port_binding_by_datapath);
++    sbrec_port_binding_index_set_datapath(target, pb->datapath);
++
++    const struct sbrec_port_binding *port;
++    SBREC_PORT_BINDING_FOR_EACH_EQUAL (port, target,
++                                       p_ctx->sbrec_port_binding_by_datapath) {
++        ofctrl_remove_flows(flow_table, &port->header_.uuid);
++        physical_eval_port_binding(p_ctx, port, flow_table);
++    }
++    sbrec_port_binding_index_destroy_row(target);
++}
++
+ void
+ physical_handle_mc_group_changes(struct physical_ctx *p_ctx,
+                                  struct ovn_desired_flow_table *flow_table)
+diff --git a/controller/physical.h b/controller/physical.h
+index dd4be7041..f0aecc852 100644
+--- a/controller/physical.h
++++ b/controller/physical.h
+@@ -81,4 +81,7 @@ bool physical_handle_flows_for_lport(const struct sbrec_port_binding *,
+                                      bool removed,
+                                      struct physical_ctx *,
+                                      struct ovn_desired_flow_table *);
++void physical_multichassis_reprocess(const struct sbrec_port_binding *,
++                                     struct physical_ctx *,
++                                     struct ovn_desired_flow_table *);
+ #endif /* controller/physical.h */
+diff --git a/controller/pinctrl.c b/controller/pinctrl.c
+index 7cbb0cf81..c86b4f940 100644
+--- a/controller/pinctrl.c
++++ b/controller/pinctrl.c
+@@ -1756,6 +1756,7 @@ pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
+         if (mtu) {
+             put_16aligned_be32(ih->icmp6_data.be32, *mtu);
+             ih->icmp6_base.icmp6_type = ICMP6_PACKET_TOO_BIG;
++            ih->icmp6_base.icmp6_code = 0;
+         }
+ 
+         void *data = ih + 1;
+diff --git a/debian/changelog b/debian/changelog
+index 8168d1e83..dc867602c 100644
+--- a/debian/changelog
++++ b/debian/changelog
+@@ -1,8 +1,14 @@
++ovn (24.09.1-1) unstable; urgency=low
++   [ OVN team ]
++   * New upstream version
++
++ -- OVN team <dev@openvswitch.org>  Fri, 13 Sep 2024 19:09:31 -0400
++
+ ovn (24.09.0-1) unstable; urgency=low
+ 
+    * New upstream version
+ 
+- -- OVN team <dev@openvswitch.org>  Fri, 09 Aug 2024 09:56:41 -0400
++ -- OVN team <dev@openvswitch.org>  Fri, 13 Sep 2024 19:09:31 -0400
+ 
+ ovn (24.03.0-1) unstable; urgency=low
+ 
+diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
+index 69bac4ab2..db17630be 100644
+--- a/ic/ovn-ic.c
++++ b/ic/ovn-ic.c
+@@ -137,7 +137,7 @@ az_run(struct ic_context *ctx)
+      * "ovn-ic-sbctl destroy avail <az>". */
+     static char *az_name;
+     const struct icsbrec_availability_zone *az;
+-    if (az_name && strcmp(az_name, nb_global->name)) {
++    if (ctx->ovnisb_txn && az_name && strcmp(az_name, nb_global->name)) {
+         ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_idl) {
+             /* AZ name update locally need to update az in ISB. */
+             if (nb_global->name[0] && !strcmp(az->name, az_name)) {
+diff --git a/include/ovn/actions.h b/include/ovn/actions.h
+index c8dd66ed8..a95a0daf7 100644
+--- a/include/ovn/actions.h
++++ b/include/ovn/actions.h
+@@ -260,6 +260,7 @@ struct ovnact_push_pop {
+ /* OVNACT_CT_NEXT. */
+ struct ovnact_ct_next {
+     struct ovnact ovnact;
++    bool dnat_zone;
+     uint8_t ltable;                /* Logical table ID of next table. */
+ };
+ 
+diff --git a/include/ovn/features.h b/include/ovn/features.h
+index 4275f7526..3566ab60f 100644
+--- a/include/ovn/features.h
++++ b/include/ovn/features.h
+@@ -30,6 +30,7 @@
+ #define OVN_FEATURE_CT_COMMIT_NAT_V2 "ct-commit-nat-v2"
+ #define OVN_FEATURE_CT_COMMIT_TO_ZONE "ct-commit-to-zone"
+ #define OVN_FEATURE_SAMPLE_WITH_REGISTERS "ovn-sample-with-registers"
++#define OVN_FEATURE_CT_NEXT_ZONE "ct-next-zone"
+ 
+ /* OVS datapath supported features.  Based on availability OVN might generate
+  * different types of openflows.
+diff --git a/lib/actions.c b/lib/actions.c
+index c12d087e7..2e05d4134 100644
+--- a/lib/actions.c
++++ b/lib/actions.c
+@@ -701,13 +701,32 @@ parse_CT_NEXT(struct action_context *ctx)
+     }
+ 
+     add_prerequisite(ctx, "ip");
+-    ovnact_put_CT_NEXT(ctx->ovnacts)->ltable = ctx->pp->cur_ltable + 1;
++    struct ovnact_ct_next *ct_next = ovnact_put_CT_NEXT(ctx->ovnacts);
++    ct_next->dnat_zone = true;
++    ct_next->ltable = ctx->pp->cur_ltable + 1;
++
++    if (!lexer_match(ctx->lexer, LEX_T_LPAREN)) {
++        return;
++    }
++
++    if (lexer_match_id(ctx->lexer, "dnat")) {
++        ct_next->dnat_zone = true;
++    } else if (lexer_match_id(ctx->lexer, "snat")) {
++        ct_next->dnat_zone = false;
++    } else {
++        lexer_error(ctx->lexer, "\"ct_next\" action accepts only"
++                                " \"dnat\" or \"snat\" parameter.");
++        return;
++    }
++
++    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+ }
+ 
+ static void
+ format_CT_NEXT(const struct ovnact_ct_next *ct_next OVS_UNUSED, struct ds *s)
+ {
+-    ds_put_cstr(s, "ct_next;");
++    ds_put_cstr(s, "ct_next");
++    ds_put_cstr(s, ct_next->dnat_zone ? "(dnat);" : "(snat);");
+ }
+ 
+ static void
+@@ -719,11 +738,17 @@ encode_CT_NEXT(const struct ovnact_ct_next *ct_next,
+ 
+     struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts);
+     ct->recirc_table = first_ptable(ep, ep->pipeline) + ct_next->ltable;
+-    ct->zone_src.field = ep->is_switch ? mf_from_id(MFF_LOG_CT_ZONE)
+-                            : mf_from_id(MFF_LOG_DNAT_ZONE);
+     ct->zone_src.ofs = 0;
+     ct->zone_src.n_bits = 16;
+ 
++    if (ep->is_switch) {
++        ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE);
++    } else {
++        ct->zone_src.field = mf_from_id(ct_next->dnat_zone
++                                        ? MFF_LOG_DNAT_ZONE
++                                        : MFF_LOG_SNAT_ZONE);
++    }
++
+     ct = ofpbuf_at_assert(ofpacts, ct_offset, sizeof *ct);
+     ofpacts->header = ct;
+     ofpact_finish_CT(ofpacts, &ct);
+diff --git a/lib/logical-fields.c b/lib/logical-fields.c
+index 2c9d3c61b..5a8b53f2b 100644
+--- a/lib/logical-fields.c
++++ b/lib/logical-fields.c
+@@ -293,6 +293,9 @@ ovn_init_symtab(struct shash *symtab)
+               "icmp6.type == {135, 136} && icmp6.code == 0 && ip.ttl == 255");
+     expr_symtab_add_predicate(symtab, "nd_ns",
+               "icmp6.type == 135 && icmp6.code == 0 && ip.ttl == 255");
++    expr_symtab_add_predicate(symtab, "nd_ns_mcast",
++              "ip6.mcast && icmp6.type == 135 && icmp6.code == 0 && "
++              "ip.ttl == 255");
+     expr_symtab_add_predicate(symtab, "nd_na",
+               "icmp6.type == 136 && icmp6.code == 0 && ip.ttl == 255");
+     expr_symtab_add_predicate(symtab, "nd_rs",
+diff --git a/lib/ovn-util.h b/lib/ovn-util.h
+index 622fec531..7b98b9b9a 100644
+--- a/lib/ovn-util.h
++++ b/lib/ovn-util.h
+@@ -38,8 +38,6 @@
+ #define STT_TUNNEL_OVERHEAD 18
+ #define VXLAN_TUNNEL_OVERHEAD 30
+ 
+-#define ECMP_NEXTHOP_IDS_LEN 65535
+-
+ struct eth_addr;
+ struct nbrec_logical_router_port;
+ struct ovsrec_flow_sample_collector_set_table;
+diff --git a/northd/en-global-config.c b/northd/en-global-config.c
+index 0ce7f8308..fff2aaa16 100644
+--- a/northd/en-global-config.c
++++ b/northd/en-global-config.c
+@@ -382,6 +382,7 @@ northd_enable_all_features(struct ed_type_global_config *data)
+         .ct_commit_nat_v2 = true,
+         .ct_commit_to_zone = true,
+         .sample_with_reg = true,
++        .ct_next_zone = true,
+     };
+ }
+ 
+@@ -452,6 +453,15 @@ build_chassis_features(const struct sbrec_chassis_table *sbrec_chassis_table,
+             chassis_features->sample_with_reg) {
+             chassis_features->sample_with_reg = false;
+         }
++
++        bool ct_next_zone =
++                smap_get_bool(&chassis->other_config,
++                              OVN_FEATURE_CT_NEXT_ZONE,
++                              false);
++        if (!ct_next_zone &&
++            chassis_features->ct_next_zone) {
++            chassis_features->ct_next_zone = false;
++        }
+     }
+ }
+ 
+diff --git a/northd/en-global-config.h b/northd/en-global-config.h
+index 0cf34482a..767810542 100644
+--- a/northd/en-global-config.h
++++ b/northd/en-global-config.h
+@@ -20,6 +20,7 @@ struct chassis_features {
+     bool ct_commit_nat_v2;
+     bool ct_commit_to_zone;
+     bool sample_with_reg;
++    bool ct_next_zone;
+ };
+ 
+ struct global_config_tracked_data {
+diff --git a/northd/en-lflow.c b/northd/en-lflow.c
+index f9d7f2459..469d7c6b5 100644
+--- a/northd/en-lflow.c
++++ b/northd/en-lflow.c
+@@ -42,7 +42,8 @@ lflow_get_input_data(struct engine_node *node,
+                      struct lflow_input *lflow_input)
+ {
+     struct northd_data *northd_data = engine_get_input_data("northd", node);
+-    struct bfd_data *bfd_data = engine_get_input_data("bfd_sync", node);
++    struct bfd_sync_data *bfd_sync_data =
++        engine_get_input_data("bfd_sync", node);
+     struct static_routes_data *static_routes_data =
+         engine_get_input_data("static_routes", node);
+     struct route_policies_data *route_policies_data =
+@@ -55,8 +56,6 @@ lflow_get_input_data(struct engine_node *node,
+         engine_get_input_data("lr_stateful", node);
+     struct ed_type_ls_stateful *ls_stateful_data =
+         engine_get_input_data("ls_stateful", node);
+-    struct ecmp_nexthop_data *nexthop_data =
+-        engine_get_input_data("ecmp_nexthop", node);
+ 
+     lflow_input->sbrec_logical_flow_table =
+         EN_OVSDB_GET(engine_get_input("SB_logical_flow", node));
+@@ -82,11 +81,10 @@ lflow_get_input_data(struct engine_node *node,
+     lflow_input->meter_groups = &sync_meters_data->meter_groups;
+     lflow_input->lb_datapaths_map = &northd_data->lb_datapaths_map;
+     lflow_input->svc_monitor_map = &northd_data->svc_monitor_map;
+-    lflow_input->bfd_connections = &bfd_data->bfd_connections;
++    lflow_input->bfd_ports = &bfd_sync_data->bfd_ports;
+     lflow_input->parsed_routes = &static_routes_data->parsed_routes;
+     lflow_input->route_tables = &static_routes_data->route_tables;
+     lflow_input->route_policies = &route_policies_data->route_policies;
+-    lflow_input->nexthops_table = &nexthop_data->nexthops;
+ 
+     struct ed_type_global_config *global_config =
+         engine_get_input_data("global_config", node);
+diff --git a/northd/en-northd.c b/northd/en-northd.c
+index 63f93bbf4..24ed31517 100644
+--- a/northd/en-northd.c
++++ b/northd/en-northd.c
+@@ -392,34 +392,15 @@ en_bfd_sync_run(struct engine_node *node, void *data)
+         = engine_get_input_data("static_routes", node);
+     const struct nbrec_bfd_table *nbrec_bfd_table =
+         EN_OVSDB_GET(engine_get_input("NB_bfd", node));
+-    struct bfd_data *bfd_sync_data = data;
++    struct bfd_sync_data *bfd_sync_data = data;
+ 
+-    bfd_destroy(data);
+-    bfd_init(data);
++    bfd_sync_destroy(data);
++    bfd_sync_init(data);
+     bfd_table_sync(eng_ctx->ovnsb_idl_txn, nbrec_bfd_table,
+                    &northd_data->lr_ports, &bfd_data->bfd_connections,
+                    &route_policies_data->bfd_active_connections,
+                    &static_routes_data->bfd_active_connections,
+-                   &bfd_sync_data->bfd_connections);
+-    engine_set_node_state(node, EN_UPDATED);
+-}
+-
+-void
+-en_ecmp_nexthop_run(struct engine_node *node, void *data)
+-{
+-    const struct engine_context *eng_ctx = engine_get_context();
+-    struct static_routes_data *static_routes_data =
+-        engine_get_input_data("static_routes", node);
+-    struct ecmp_nexthop_data *enh_data = data;
+-    const struct sbrec_ecmp_nexthop_table *sbrec_ecmp_nexthop_table =
+-        EN_OVSDB_GET(engine_get_input("SB_ecmp_nexthop", node));
+-
+-    ecmp_nexthop_destroy(data);
+-    ecmp_nexthop_init(data);
+-    build_ecmp_nexthop_table(eng_ctx->ovnsb_idl_txn,
+-                             &static_routes_data->parsed_routes,
+-                             &enh_data->nexthops,
+-                             sbrec_ecmp_nexthop_table);
++                   &bfd_sync_data->bfd_ports);
+     engine_set_node_state(node, EN_UPDATED);
+ }
+ 
+@@ -468,18 +449,8 @@ void
+ *en_bfd_sync_init(struct engine_node *node OVS_UNUSED,
+                   struct engine_arg *arg OVS_UNUSED)
+ {
+-    struct bfd_data *data = xzalloc(sizeof *data);
+-    bfd_init(data);
+-    return data;
+-}
+-
+-void
+-*en_ecmp_nexthop_init(struct engine_node *node OVS_UNUSED,
+-                      struct engine_arg *arg OVS_UNUSED)
+-{
+-    struct ecmp_nexthop_data *data = xzalloc(sizeof *data);
+-
+-    ecmp_nexthop_init(data);
++    struct bfd_sync_data *data = xzalloc(sizeof *data);
++    bfd_sync_init(data);
+     return data;
+ }
+ 
+@@ -553,11 +524,5 @@ en_bfd_cleanup(void *data)
+ void
+ en_bfd_sync_cleanup(void *data)
+ {
+-    bfd_destroy(data);
+-}
+-
+-void
+-en_ecmp_nexthop_cleanup(void *data)
+-{
+-    ecmp_nexthop_destroy(data);
++    bfd_sync_destroy(data);
+ }
+diff --git a/northd/en-northd.h b/northd/en-northd.h
+index 2666cc67e..631a7c17a 100644
+--- a/northd/en-northd.h
++++ b/northd/en-northd.h
+@@ -42,9 +42,5 @@ bool bfd_sync_northd_change_handler(struct engine_node *node,
+                                     void *data OVS_UNUSED);
+ void en_bfd_sync_run(struct engine_node *node, void *data);
+ void en_bfd_sync_cleanup(void *data OVS_UNUSED);
+-void en_ecmp_nexthop_run(struct engine_node *node, void *data);
+-void *en_ecmp_nexthop_init(struct engine_node *node OVS_UNUSED,
+-                           struct engine_arg *arg OVS_UNUSED);
+-void en_ecmp_nexthop_cleanup(void *data);
+ 
+ #endif /* EN_NORTHD_H */
+diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
+index cb880b439..1f79916a5 100644
+--- a/northd/inc-proc-northd.c
++++ b/northd/inc-proc-northd.c
+@@ -103,8 +103,7 @@ static unixctl_cb_func chassis_features_list;
+     SB_NODE(fdb, "fdb") \
+     SB_NODE(static_mac_binding, "static_mac_binding") \
+     SB_NODE(chassis_template_var, "chassis_template_var") \
+-    SB_NODE(logical_dp_group, "logical_dp_group") \
+-    SB_NODE(ecmp_nexthop, "ecmp_nexthop")
++    SB_NODE(logical_dp_group, "logical_dp_group")
+ 
+ enum sb_engine_node {
+ #define SB_NODE(NAME, NAME_STR) SB_##NAME,
+@@ -163,7 +162,6 @@ static ENGINE_NODE(route_policies, "route_policies");
+ static ENGINE_NODE(static_routes, "static_routes");
+ static ENGINE_NODE(bfd, "bfd");
+ static ENGINE_NODE(bfd_sync, "bfd_sync");
+-static ENGINE_NODE(ecmp_nexthop, "ecmp_nexthop");
+ 
+ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
+                           struct ovsdb_idl_loop *sb)
+@@ -266,9 +264,6 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
+     engine_add_input(&en_bfd_sync, &en_route_policies, NULL);
+     engine_add_input(&en_bfd_sync, &en_northd, bfd_sync_northd_change_handler);
+ 
+-    engine_add_input(&en_ecmp_nexthop, &en_sb_ecmp_nexthop, NULL);
+-    engine_add_input(&en_ecmp_nexthop, &en_static_routes, NULL);
+-
+     engine_add_input(&en_sync_meters, &en_nb_acl, NULL);
+     engine_add_input(&en_sync_meters, &en_nb_meter, NULL);
+     engine_add_input(&en_sync_meters, &en_sb_meter, NULL);
+@@ -282,7 +277,6 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
+     engine_add_input(&en_lflow, &en_bfd_sync, NULL);
+     engine_add_input(&en_lflow, &en_route_policies, NULL);
+     engine_add_input(&en_lflow, &en_static_routes, NULL);
+-    engine_add_input(&en_lflow, &en_ecmp_nexthop, NULL);
+     engine_add_input(&en_lflow, &en_global_config,
+                      node_global_config_handler);
+ 
+diff --git a/northd/northd.c b/northd/northd.c
+index 5ad30d854..2c4703301 100644
+--- a/northd/northd.c
++++ b/northd/northd.c
+@@ -1126,7 +1126,7 @@ is_l3dgw_port(const struct ovn_port *op)
+ /* This function returns true if 'op' is a chassis resident
+  * derived port. False otherwise.
+  * There are 2 ways to check if 'op' is chassis resident port.
+- *  1. op->sb->type is "chassisresident"
++ *  1. op->sb->type is "chassisredirect"
+  *  2. op->primary_port is not NULL.  If op->primary_port is set,
+  *     it means 'op' is derived from the ovn_port op->primary_port.
+  *
+@@ -2136,7 +2136,7 @@ create_cr_port(struct ovn_port *op, struct hmap *ports,
+ 
+     struct ovn_port *crp = ovn_port_find(ports, redirect_name);
+     if (crp && crp->sb && crp->sb->datapath == op->od->sb) {
+-        ovn_port_set_nb(crp, NULL, op->nbrp);
++        ovn_port_set_nb(crp, op->nbsp, op->nbrp);
+         ovs_list_remove(&crp->list);
+         ovs_list_push_back(both_dbs, &crp->list);
+     } else {
+@@ -2466,7 +2466,7 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table,
+     }
+ 
+ 
+-    /* Create chassisresident port for the distributed gateway port's (DGP)
++    /* Create chassisredirect port for the distributed gateway port's (DGP)
+      * peer if
+      *  - DGP's router has only one DGP and
+      *  - Its peer is a logical switch port and
+@@ -9633,16 +9633,21 @@ build_lswitch_arp_nd_responder_known_ips(struct ovn_port *op,
+                                                   op->lflow_ref);
+             }
+ 
+-            /* For ND solicitations, we need to listen for both the
+-             * unicast IPv6 address and its all-nodes multicast address,
+-             * but always respond with the unicast IPv6 address. */
++            /* For ND solicitations:
++             *   - Reply only for the all-nodes multicast address(es) of the
++             *     logical port IPv6 address(es).
++             *
++             *   - Do not reply for unicast ND solicitations.  Let the target
++             *     reply to it, so that the sender has the ability to monitor
++             *     the target liveness via the unicast ND solicitations.
++             */
+             for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
+                 ds_clear(match);
+-                ds_put_format(match,
+-                        "nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
+-                        op->lsp_addrs[i].ipv6_addrs[j].addr_s,
+-                        op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s,
+-                        op->lsp_addrs[i].ipv6_addrs[j].addr_s);
++                ds_put_format(
++                    match,
++                    "nd_ns_mcast && ip6.dst == %s && nd.target == %s",
++                    op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s,
++                    op->lsp_addrs[i].ipv6_addrs[j].addr_s);
+ 
+                 ds_clear(actions);
+                 ds_put_format(actions,
+@@ -10437,18 +10442,11 @@ bfd_port_lookup(const struct hmap *bfd_map, const char *logical_port,
+ }
+ 
+ static bool
+-bfd_is_port_running(const struct hmap *bfd_map, const char *port)
++bfd_is_port_running(const struct sset *bfd_ports, const char *port)
+ {
+-    struct bfd_entry *bfd_e;
+-    HMAP_FOR_EACH (bfd_e, hmap_node, bfd_map) {
+-        if (!strcmp(bfd_e->logical_port, port)) {
+-            return true;
+-        }
+-    }
+-    return false;
++    return !!sset_find(bfd_ports, port);
+ }
+ 
+-
+ #define BFD_DEF_MINTX       1000 /* 1s */
+ #define BFD_DEF_MINRX       1000 /* 1s */
+ #define BFD_DEF_DETECT_MULT 5
+@@ -10536,17 +10534,18 @@ bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
+                const struct hmap *bfd_connections,
+                const struct hmap *rp_bfd_connections,
+                const struct hmap *sr_bfd_connections,
+-               struct hmap *sync_bfd_connections)
++               struct sset *bfd_ports)
+ {
+     if (!ovnsb_txn) {
+         return;
+     }
+ 
+     unsigned long *bfd_src_ports = bitmap_allocate(BFD_UDP_SRC_PORT_LEN);
++    struct hmap sync_bfd_connections = HMAP_INITIALIZER(&sync_bfd_connections);
+ 
+     struct bfd_entry *bfd_e;
+     HMAP_FOR_EACH (bfd_e, hmap_node, bfd_connections) {
+-        struct bfd_entry *e = bfd_alloc_entry(sync_bfd_connections,
++        struct bfd_entry *e = bfd_alloc_entry(&sync_bfd_connections,
+                                               bfd_e->logical_port,
+                                               bfd_e->dst_ip, bfd_e->status);
+         e->nb_bt = bfd_e->nb_bt;
+@@ -10561,7 +10560,7 @@ bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
+ 
+     const struct nbrec_bfd *nb_bt;
+     NBREC_BFD_TABLE_FOR_EACH (nb_bt, nbrec_bfd_table) {
+-        bfd_e = bfd_port_lookup(sync_bfd_connections, nb_bt->logical_port,
++        bfd_e = bfd_port_lookup(&sync_bfd_connections, nb_bt->logical_port,
+                                 nb_bt->dst_ip);
+         if (!bfd_e) {
+             continue;
+@@ -10619,16 +10618,17 @@ bfd_table_sync(struct ovsdb_idl_txn *ovnsb_txn,
+             }
+         }
+ 
++        sset_add(bfd_ports, nb_bt->logical_port);
+         bfd_e->stale = false;
+     }
+ 
+-    HMAP_FOR_EACH_SAFE (bfd_e, hmap_node, sync_bfd_connections) {
++    HMAP_FOR_EACH_POP (bfd_e, hmap_node, &sync_bfd_connections) {
+         if (bfd_e->stale) {
+-            hmap_remove(sync_bfd_connections, &bfd_e->hmap_node);
+             sbrec_bfd_delete(bfd_e->sb_bt);
+-            bfd_erase_entry(bfd_e);
+         }
++        bfd_erase_entry(bfd_e);
+     }
++    hmap_destroy(&sync_bfd_connections);
+ 
+     bitmap_free(bfd_src_ports);
+ }
+@@ -10665,64 +10665,6 @@ build_bfd_map(const struct nbrec_bfd_table *nbrec_bfd_table,
+     }
+ }
+ 
+-void
+-build_ecmp_nexthop_table(
+-        struct ovsdb_idl_txn *ovnsb_txn,
+-        struct hmap *routes,
+-        struct simap *nexthops,
+-        const struct sbrec_ecmp_nexthop_table *sbrec_ecmp_nexthop_table)
+-{
+-    if (!ovnsb_txn) {
+-        return;
+-    }
+-
+-    unsigned long *nexthop_ids = bitmap_allocate(ECMP_NEXTHOP_IDS_LEN);
+-    const struct sbrec_ecmp_nexthop *sb_ecmp_nexthop;
+-    SBREC_ECMP_NEXTHOP_TABLE_FOR_EACH (sb_ecmp_nexthop,
+-                                       sbrec_ecmp_nexthop_table) {
+-        simap_put(nexthops, sb_ecmp_nexthop->nexthop,
+-                  sb_ecmp_nexthop->id);
+-        bitmap_set1(nexthop_ids, sb_ecmp_nexthop->id);
+-    }
+-
+-    struct sset nb_nexthops_sset = SSET_INITIALIZER(&nb_nexthops_sset);
+-
+-    struct parsed_route *pr;
+-    HMAP_FOR_EACH (pr, key_node, routes) {
+-        if (!pr->ecmp_symmetric_reply) {
+-            continue;
+-        }
+-
+-        const struct nbrec_logical_router_static_route *r = pr->route;
+-        if (!simap_contains(nexthops, r->nexthop)) {
+-            int id = bitmap_scan(nexthop_ids, 0, 1, ECMP_NEXTHOP_IDS_LEN);
+-            if (id == ECMP_NEXTHOP_IDS_LEN) {
+-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+-                VLOG_WARN_RL(&rl, "nexthop id address space is exhausted");
+-                continue;
+-            }
+-            bitmap_set1(nexthop_ids, id);
+-            simap_put(nexthops, r->nexthop, id);
+-
+-            sb_ecmp_nexthop = sbrec_ecmp_nexthop_insert(ovnsb_txn);
+-            sbrec_ecmp_nexthop_set_nexthop(sb_ecmp_nexthop, r->nexthop);
+-            sbrec_ecmp_nexthop_set_id(sb_ecmp_nexthop, id);
+-        }
+-        sset_add(&nb_nexthops_sset, r->nexthop);
+-    }
+-
+-    SBREC_ECMP_NEXTHOP_TABLE_FOR_EACH_SAFE (sb_ecmp_nexthop,
+-                                            sbrec_ecmp_nexthop_table) {
+-        if (!sset_contains(&nb_nexthops_sset, sb_ecmp_nexthop->nexthop)) {
+-            simap_find_and_delete(nexthops, sb_ecmp_nexthop->nexthop);
+-            sbrec_ecmp_nexthop_delete(sb_ecmp_nexthop);
+-        }
+-    }
+-
+-    sset_destroy(&nb_nexthops_sset);
+-    bitmap_free(nexthop_ids);
+-}
+-
+ /* Returns a string of the IP address of the router port 'op' that
+  * overlaps with 'ip_s".  If one is not found, returns NULL.
+  *
+@@ -11113,7 +11055,7 @@ parsed_route_lookup(struct hmap *routes, size_t hash,
+ static void
+ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
+                   const struct nbrec_logical_router_static_route *route,
+-                  struct hmap *bfd_connections,
++                  const struct hmap *bfd_connections,
+                   struct hmap *routes, struct simap *route_tables,
+                   struct hmap *bfd_active_connections)
+ {
+@@ -11226,7 +11168,7 @@ parsed_routes_add(struct ovn_datapath *od, const struct hmap *lr_ports,
+ 
+ void
+ build_parsed_routes(struct ovn_datapath *od, const struct hmap *lr_ports,
+-                    struct hmap *bfd_connections, struct hmap *routes,
++                    const struct hmap *bfd_connections, struct hmap *routes,
+                     struct simap *route_tables,
+                     struct hmap *bfd_active_connections)
+ {
+@@ -11512,8 +11454,7 @@ add_ecmp_symmetric_reply_flows(struct lflow_table *lflows,
+                                struct ovn_port *out_port,
+                                const struct parsed_route *route,
+                                struct ds *route_match,
+-                               struct lflow_ref *lflow_ref,
+-                               struct simap *nexthops_table)
++                               struct lflow_ref *lflow_ref)
+ {
+     const struct nbrec_logical_router_static_route *st_route = route->route;
+     struct ds match = DS_EMPTY_INITIALIZER;
+@@ -11548,15 +11489,9 @@ add_ecmp_symmetric_reply_flows(struct lflow_table *lflows,
+     ds_put_cstr(&match, " && (ct.new || ct.est)");
+     ds_put_format(&actions,
+             "ct_commit { ct_label.ecmp_reply_eth = eth.src; "
+-            "ct_mark.ecmp_reply_port = %" PRId64 ";",
++            "ct_mark.ecmp_reply_port = %" PRId64 ";}; "
++            "next;",
+             out_port->sb->tunnel_key);
+-
+-    struct simap_node *n = simap_find(nexthops_table, st_route->nexthop);
+-    if (n) {
+-        ds_put_format(&actions, " ct_label.label = %d;", n->data);
+-    }
+-    ds_put_cstr(&actions, " }; next;");
+-
+     ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100,
+                             ds_cstr(&match), ds_cstr(&actions),
+                             &st_route->header_,
+@@ -11613,8 +11548,7 @@ add_ecmp_symmetric_reply_flows(struct lflow_table *lflows,
+ static void
+ build_ecmp_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
+                       const struct hmap *lr_ports, struct ecmp_groups_node *eg,
+-                      struct lflow_ref *lflow_ref,
+-                      struct simap *nexthops_table)
++                      struct lflow_ref *lflow_ref)
+ 
+ {
+     bool is_ipv4 = IN6_IS_ADDR_V4MAPPED(&eg->prefix);
+@@ -11631,21 +11565,27 @@ build_ecmp_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
+ 
+     struct ds actions = DS_EMPTY_INITIALIZER;
+     ds_put_format(&actions, "ip.ttl--; flags.loopback = 1; %s = %"PRIu16
+-                  "; %s = select(", REG_ECMP_GROUP_ID, eg->id,
+-                  REG_ECMP_MEMBER_ID);
++                  "; %s = ", REG_ECMP_GROUP_ID, eg->id, REG_ECMP_MEMBER_ID);
+ 
+-    bool is_first = true;
+-    LIST_FOR_EACH (er, list_node, &eg->route_list) {
+-        if (is_first) {
+-            is_first = false;
+-        } else {
+-            ds_put_cstr(&actions, ", ");
++    if (!ovs_list_is_singleton(&eg->route_list)) {
++        bool is_first = true;
++
++        ds_put_cstr(&actions, "select(");
++        LIST_FOR_EACH (er, list_node, &eg->route_list) {
++            if (is_first) {
++                is_first = false;
++            } else {
++                ds_put_cstr(&actions, ", ");
++            }
++            ds_put_format(&actions, "%"PRIu16, er->id);
+         }
+-        ds_put_format(&actions, "%"PRIu16, er->id);
++        ds_put_cstr(&actions, ");");
++    } else {
++        er = CONTAINER_OF(ovs_list_front(&eg->route_list),
++                          struct ecmp_route_list_node, list_node);
++        ds_put_format(&actions, "%"PRIu16"; next;", er->id);
+     }
+ 
+-    ds_put_cstr(&actions, ");");
+-
+     ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, priority,
+                   ds_cstr(&route_match), ds_cstr(&actions),
+                   lflow_ref);
+@@ -11671,7 +11611,7 @@ build_ecmp_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
+                                                      out_port->key)) {
+             add_ecmp_symmetric_reply_flows(lflows, od, lrp_addr_s, out_port,
+                                            route_, &route_match,
+-                                           lflow_ref, nexthops_table);
++                                           lflow_ref);
+         }
+         ds_clear(&match);
+         ds_put_format(&match, REG_ECMP_GROUP_ID" == %"PRIu16" && "
+@@ -11704,7 +11644,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
+           const struct ovn_port *op, const char *lrp_addr_s,
+           const char *network_s, int plen, const char *gateway,
+           bool is_src_route, const uint32_t rtb_id,
+-          const struct hmap *bfd_connections,
++          const struct sset *bfd_ports,
+           const struct ovsdb_idl_row *stage_hint, bool is_discard_route,
+           int ofs, struct lflow_ref *lflow_ref)
+ {
+@@ -11753,7 +11693,7 @@ add_route(struct lflow_table *lflows, struct ovn_datapath *od,
+                             priority, ds_cstr(&match),
+                             ds_cstr(&actions), stage_hint,
+                             lflow_ref);
+-    if (op && bfd_is_port_running(bfd_connections, op->key)) {
++    if (op && bfd_is_port_running(bfd_ports, op->key)) {
+         ds_put_format(&match, " && udp.dst == 3784");
+         ovn_lflow_add_with_hint(lflows, op->od,
+                                 S_ROUTER_IN_IP_ROUTING,
+@@ -11770,7 +11710,7 @@ static void
+ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
+                         const struct hmap *lr_ports,
+                         const struct parsed_route *route_,
+-                        const struct hmap *bfd_connections,
++                        const struct sset *bfd_ports,
+                         struct lflow_ref *lflow_ref)
+ {
+     const char *lrp_addr_s = NULL;
+@@ -11795,7 +11735,7 @@ build_static_route_flow(struct lflow_table *lflows, struct ovn_datapath *od,
+     add_route(lflows, route_->is_discard_route ? od : out_port->od, out_port,
+               lrp_addr_s, prefix_s, route_->plen, route->nexthop,
+               route_->is_src_route, route_->route_table_id,
+-              bfd_connections, &route->header_, route_->is_discard_route,
++              bfd_ports, &route->header_, route_->is_discard_route,
+               ofs, lflow_ref);
+ 
+     free(prefix_s);
+@@ -12617,7 +12557,7 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
+ 
+     if (op->peer && op->peer->cr_port) {
+         /* We don't add the below flows if the router port's peer has
+-         * a chassisresident port.  That's because routing is centralized on
++         * a chassisredirect port.  That's because routing is centralized on
+          * the gateway chassis for the router port networks/subnets.
+          */
+         return;
+@@ -12947,10 +12887,10 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op,
+ static void
+ build_lrouter_bfd_flows(struct lflow_table *lflows, struct ovn_port *op,
+                         const struct shash *meter_groups,
+-                        const struct hmap *bfd_connections,
++                        const struct sset *bfd_ports,
+                         struct lflow_ref *lflow_ref)
+ {
+-    if (!bfd_is_port_running(bfd_connections, op->key)) {
++    if (!bfd_is_port_running(bfd_ports, op->key)) {
+         return;
+     }
+ 
+@@ -13546,7 +13486,7 @@ build_ip_routing_pre_flows_for_lrouter(struct ovn_datapath *od,
+  */
+ static void
+ build_ip_routing_flows_for_lrp(struct ovn_port *op,
+-                               const struct hmap *bfd_connections,
++                               const struct sset *bfd_ports,
+                                struct lflow_table *lflows,
+                                struct lflow_ref *lflow_ref)
+ {
+@@ -13555,7 +13495,7 @@ build_ip_routing_flows_for_lrp(struct ovn_port *op,
+         add_route(lflows, op->od, op, op->lrp_networks.ipv4_addrs[i].addr_s,
+                   op->lrp_networks.ipv4_addrs[i].network_s,
+                   op->lrp_networks.ipv4_addrs[i].plen, NULL, false, 0,
+-                  bfd_connections, &op->nbrp->header_, false,
++                  bfd_ports, &op->nbrp->header_, false,
+                   ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
+     }
+ 
+@@ -13563,7 +13503,7 @@ build_ip_routing_flows_for_lrp(struct ovn_port *op,
+         add_route(lflows, op->od, op, op->lrp_networks.ipv6_addrs[i].addr_s,
+                   op->lrp_networks.ipv6_addrs[i].network_s,
+                   op->lrp_networks.ipv6_addrs[i].plen, NULL, false, 0,
+-                  bfd_connections, &op->nbrp->header_, false,
++                  bfd_ports, &op->nbrp->header_, false,
+                   ROUTE_PRIO_OFFSET_CONNECTED, lflow_ref);
+     }
+ }
+@@ -13572,8 +13512,8 @@ static void
+ build_static_route_flows_for_lrouter(
+         struct ovn_datapath *od, struct lflow_table *lflows,
+         const struct hmap *lr_ports, struct hmap *parsed_routes,
+-        struct simap *route_tables, const struct hmap *bfd_connections,
+-        struct lflow_ref *lflow_ref, struct simap *nexthops_table)
++        struct simap *route_tables, const struct sset *bfd_ports,
++        struct lflow_ref *lflow_ref)
+ {
+     ovs_assert(od->nbr);
+     ovn_lflow_add_default_drop(lflows, od, S_ROUTER_IN_IP_ROUTING_ECMP,
+@@ -13607,6 +13547,11 @@ build_static_route_flows_for_lrouter(
+                 if (group) {
+                     ecmp_groups_add_route(group, route);
+                 }
++            } else if (route->ecmp_symmetric_reply) {
++                /* Traffic for symmetric reply routes has to be conntracked
++                 * even if there is only one next-hop, in case another next-hop
++                 * is added later. */
++                ecmp_groups_add(&ecmp_groups, route);
+             } else {
+                 unique_routes_add(&unique_routes, route);
+             }
+@@ -13615,13 +13560,12 @@ build_static_route_flows_for_lrouter(
+     HMAP_FOR_EACH (group, hmap_node, &ecmp_groups) {
+         /* add a flow in IP_ROUTING, and one flow for each member in
+          * IP_ROUTING_ECMP. */
+-        build_ecmp_route_flow(lflows, od, lr_ports, group, lflow_ref,
+-                              nexthops_table);
++        build_ecmp_route_flow(lflows, od, lr_ports, group, lflow_ref);
+     }
+     const struct unique_routes_node *ur;
+     HMAP_FOR_EACH (ur, hmap_node, &unique_routes) {
+         build_static_route_flow(lflows, od, lr_ports, ur->route,
+-                                bfd_connections, lflow_ref);
++                                bfd_ports, lflow_ref);
+     }
+     ecmp_groups_destroy(&ecmp_groups);
+     unique_routes_destroy(&unique_routes);
+@@ -14002,6 +13946,234 @@ build_arp_resolve_flows_for_lrp(struct ovn_port *op,
+     }
+ }
+ 
++static void
++build_routing_protocols_redirect_rule__(
++        const char *s_addr, const char *redirect_port_name, int protocol_port,
++        const char *proto, bool is_ipv6, struct ovn_port *ls_peer,
++        struct lflow_table *lflows, struct ds *match, struct ds *actions,
++        struct lflow_ref *lflow_ref)
++{
++    int ip_ver = is_ipv6 ? 6 : 4;
++    ds_clear(actions);
++    ds_put_format(actions, "outport = \"%s\"; output;", redirect_port_name);
++
++    /* Redirect packets in the input pipeline destined for LR's IP
++     * and the routing protocol's port to the LSP specified in
++     * 'routing-protocol-redirect' option.*/
++    ds_clear(match);
++    ds_put_format(match, "ip%d.dst == %s && %s.dst == %d", ip_ver, s_addr,
++                  proto, protocol_port);
++    ovn_lflow_add(lflows, ls_peer->od, S_SWITCH_IN_L2_LKUP, 100,
++                  ds_cstr(match),
++                  ds_cstr(actions),
++                  lflow_ref);
++
++    /* To accomodate "peer" nature of the routing daemons, redirect also
++     * replies to the daemons' client requests. */
++    ds_clear(match);
++    ds_put_format(match, "ip%d.dst == %s && %s.src == %d", ip_ver, s_addr,
++                  proto, protocol_port);
++    ovn_lflow_add(lflows, ls_peer->od, S_SWITCH_IN_L2_LKUP, 100,
++                  ds_cstr(match),
++                  ds_cstr(actions),
++                  lflow_ref);
++}
++
++static void
++apply_routing_protocols_redirect__(
++        const char *s_addr, const char *redirect_port_name, int protocol_flags,
++        bool is_ipv6, struct ovn_port *ls_peer, struct lflow_table *lflows,
++        struct ds *match, struct ds *actions, struct lflow_ref *lflow_ref)
++{
++    if (protocol_flags & REDIRECT_BGP) {
++        build_routing_protocols_redirect_rule__(s_addr, redirect_port_name,
++                                                179, "tcp", is_ipv6, ls_peer,
++                                                lflows, match, actions,
++                                                lflow_ref);
++    }
++
++    if (protocol_flags & REDIRECT_BFD) {
++        build_routing_protocols_redirect_rule__(s_addr, redirect_port_name,
++                                                3784, "udp", is_ipv6, ls_peer,
++                                                lflows, match, actions,
++                                                lflow_ref);
++    }
++
++    /* Because the redirected port shares IP and MAC addresses with the LRP,
++     * special consideration needs to be given to the signaling protocols. */
++    ds_clear(actions);
++    ds_put_format(actions,
++                 "clone { outport = \"%s\"; output; }; "
++                 "outport = %s; output;",
++                  redirect_port_name, ls_peer->json_key);
++    if (is_ipv6) {
++        /* Ensure that redirect port receives copy of NA messages destined to
++         * its IP.*/
++        ds_clear(match);
++        ds_put_format(match, "ip6.dst == %s && nd_na", s_addr);
++        ovn_lflow_add(lflows, ls_peer->od, S_SWITCH_IN_L2_LKUP, 100,
++                      ds_cstr(match),
++                      ds_cstr(actions),
++                      lflow_ref);
++    } else {
++        /* Ensure that redirect port receives copy of ARP replies destined to
++         * its IP */
++        ds_clear(match);
++        ds_put_format(match, "arp.op == 2 && arp.tpa == %s", s_addr);
++        ovn_lflow_add(lflows, ls_peer->od, S_SWITCH_IN_L2_LKUP, 100,
++                      ds_cstr(match),
++                      ds_cstr(actions),
++                      lflow_ref);
++    }
++}
++
++static int
++parse_redirected_routing_protocols(struct ovn_port *lrp) {
++    int redirected_protocol_flags = 0;
++    const char *redirect_protocols = smap_get(&lrp->nbrp->options,
++                                              "routing-protocols");
++    if (!redirect_protocols) {
++        return redirected_protocol_flags;
++    }
++
++    char *proto;
++    char *save_ptr = NULL;
++    char *tokstr = xstrdup(redirect_protocols);
++    for (proto = strtok_r(tokstr, ",", &save_ptr); proto != NULL;
++         proto = strtok_r(NULL, ",", &save_ptr)) {
++        if (!strcmp(proto, "BGP")) {
++            redirected_protocol_flags |= REDIRECT_BGP;
++            continue;
++        }
++
++        if (!strcmp(proto, "BFD")) {
++            redirected_protocol_flags |= REDIRECT_BFD;
++            continue;
++        }
++
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++        VLOG_WARN_RL(&rl, "Option 'routing-protocols' encountered unknown "
++                          "value %s",
++                          proto);
++    }
++    free(tokstr);
++    return redirected_protocol_flags;
++}
++
++static void
++build_lrouter_routing_protocol_redirect(
++        struct ovn_port *op, struct lflow_table *lflows, struct ds *match,
++        struct ds *actions, struct lflow_ref *lflow_ref,
++        const struct hmap *ls_ports)
++{
++    /* LRP has to have a peer.*/
++    if (!op->peer) {
++        return;
++    }
++
++    /* LRP has to have NB record.*/
++    if (!op->nbrp) {
++        return;
++    }
++
++    /* Proceed only for LRPs that have 'routing-protocol-redirect' option set.
++     * Value of this option is the name of LSP to which the routing protocol
++     * traffic will be redirected. */
++    const char *redirect_port_name = smap_get(&op->nbrp->options,
++                                              "routing-protocol-redirect");
++    if (!redirect_port_name) {
++        return;
++    }
++
++    if (op->cr_port) {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++        VLOG_WARN_RL(&rl, "Option 'routing-protocol-redirect' is not "
++                          "supported on Distributed Gateway Port '%s'",
++                          op->key);
++        return;
++    }
++
++    /* Ensure that LSP, to which the routing protocol traffic is redirected,
++     * exists. */
++    struct ovn_port *lsp_in_peer = ovn_port_find(ls_ports,
++                                                 redirect_port_name);
++    if (!lsp_in_peer) {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++        VLOG_WARN_RL(&rl, "Option 'routing-protocol-redirect' set on Logical "
++                          "Router Port '%s' refers to non-existent Logical "
++                          "Switch Port. Routing protocol redirecting won't be "
++                          "configured.",
++                          op->key);
++        return;
++    }
++    if (lsp_in_peer->od != op->peer->od) {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++        VLOG_WARN_RL(&rl, "Logical Router Port '%s' is connected to a "
++                          "different switch than the Logical Switch Port "
++                          "'%s' defined in its 'routing-protocol-redirect' "
++                          "option. Routing protocol redirecting won't be "
++                          "configured.",
++                          op->key, redirect_port_name);
++        return;
++    }
++
++    int redirected_protocols = parse_redirected_routing_protocols(op);
++    if (!redirected_protocols) {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++        VLOG_WARN_RL(&rl, "Option 'routing-protocol-redirect' is set on "
++                          "Logical Router Port '%s' but no known protocols "
++                          "were set via 'routing-protocols' options. This "
++                          "configuration has no effect.",
++                          op->key);
++        return;
++    }
++
++    /* Redirect traffic destined for LRP's IPs and the specified routing
++     * protocol ports to the port defined in 'routing-protocol-redirect'
++     * option.*/
++    for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
++        const char *ip_s = op->lrp_networks.ipv4_addrs[i].addr_s;
++        apply_routing_protocols_redirect__(ip_s, redirect_port_name,
++                                           redirected_protocols, false,
++                                           op->peer, lflows, match, actions,
++                                           lflow_ref);
++    }
++    for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
++        const char *ip_s = op->lrp_networks.ipv6_addrs[i].addr_s;
++        apply_routing_protocols_redirect__(ip_s, redirect_port_name,
++                                           redirected_protocols, true,
++                                           op->peer, lflows, match, actions,
++                                           lflow_ref);
++    }
++
++    /* Drop ARP replies and IPv6 RA/NA packets originating from
++     * 'routing-protocol-redirect' LSP. As this port shares IP and MAC
++     * addresses with LRP, we don't want to create duplicates.*/
++    ds_clear(match);
++    ds_put_format(match, "inport == \"%s\" && arp.op == 2",
++                  redirect_port_name);
++    ovn_lflow_add(lflows, op->peer->od, S_SWITCH_IN_CHECK_PORT_SEC, 80,
++                  ds_cstr(match),
++                  REGBIT_PORT_SEC_DROP " = 1; next;",
++                  lflow_ref);
++
++    ds_clear(match);
++    ds_put_format(match, "inport == \"%s\" && nd_na",
++                  redirect_port_name);
++    ovn_lflow_add(lflows, op->peer->od, S_SWITCH_IN_CHECK_PORT_SEC, 80,
++                  ds_cstr(match),
++                  REGBIT_PORT_SEC_DROP " = 1; next;",
++                  lflow_ref);
++
++    ds_clear(match);
++    ds_put_format(match, "inport == \"%s\" && nd_ra",
++                  redirect_port_name);
++    ovn_lflow_add(lflows, op->peer->od, S_SWITCH_IN_CHECK_PORT_SEC, 80,
++                  ds_cstr(match),
++                  REGBIT_PORT_SEC_DROP " = 1; next;",
++                  lflow_ref);
++}
++
+ /* This function adds ARP resolve flows related to a LSP. */
+ static void
+ build_arp_resolve_flows_for_lsp(
+@@ -15095,7 +15267,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
+                             struct lflow_table *lflows,
+                             struct ds *match, struct ds *actions,
+                             const struct shash *meter_groups,
+-                            const struct hmap *bfd_connections,
++                            const struct sset *bfd_ports,
+                             struct lflow_ref *lflow_ref)
+ {
+     ovs_assert(op->nbrp);
+@@ -15137,8 +15309,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
+     }
+ 
+     /* BFD msg handling */
+-    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_connections,
+-                            lflow_ref);
++    build_lrouter_bfd_flows(lflows, op, meter_groups, bfd_ports, lflow_ref);
+ 
+     /* ICMP time exceeded */
+     struct ds ip_ds = DS_EMPTY_INITIALIZER;
+@@ -15824,7 +15995,6 @@ build_lrouter_out_snat_flow(struct lflow_table *lflows,
+     build_lrouter_out_snat_match(lflows, od, nat, match, distributed_nat,
+                                  cidr_bits, is_v6, l3dgw_port, lflow_ref,
+                                  false);
+-    size_t original_match_len = match->length;
+ 
+     if (!od->is_gw_router && distributed_nat) {
+         ds_put_format(actions, "eth.src = "ETH_ADDR_FMT"; ",
+@@ -15846,14 +16016,13 @@ build_lrouter_out_snat_flow(struct lflow_table *lflows,
+     /* For the SNAT networks, we need to make sure that connections are
+      * properly tracked so we can decide whether to perform SNAT on traffic
+      * exiting the network. */
+-    if (features->ct_commit_to_zone && !strcmp(nat->type, "snat") &&
+-        !od->is_gw_router) {
++    if (features->ct_commit_to_zone && features->ct_next_zone &&
++        !strcmp(nat->type, "snat") && !od->is_gw_router) {
+         /* For traffic that comes from SNAT network, initiate CT state before
+          * entering S_ROUTER_OUT_SNAT to allow matching on various CT states.
+          */
+-        ds_truncate(match, original_match_len);
+         ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 70,
+-                      ds_cstr(match), "ct_snat;",
++                      ds_cstr(match), "ct_next(snat);",
+                       lflow_ref);
+ 
+         build_lrouter_out_snat_match(lflows, od, nat, match,
+@@ -16116,7 +16285,7 @@ lrouter_check_nat_entry(const struct ovn_datapath *od,
+     *distributed = false;
+ 
+     /* NAT cannnot be distributed if the DGP's peer
+-     * has a chassisresident port (as the routing is centralized
++     * has a chassisredirect port (as the routing is centralized
+      * on the gateway chassis for the DGP's networks/subnets.)
+      */
+     struct ovn_port *l3dgw_port = *nat_l3dgw_port;
+@@ -16591,7 +16760,7 @@ build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
+ static void
+ build_routable_flows_for_router_port(
+     struct ovn_port *lrp, const struct lr_stateful_record *lr_stateful_rec,
+-    const struct hmap *bfd_connections,
++    const struct sset *bfd_ports,
+     struct lflow_table *lflows,
+     struct ds *match,
+     struct ds *actions)
+@@ -16628,7 +16797,7 @@ build_routable_flows_for_router_port(
+                               router_port->lrp_networks.ipv4_addrs[0].addr_s,
+                               laddrs->ipv4_addrs[k].network_s,
+                               laddrs->ipv4_addrs[k].plen, NULL, false, 0,
+-                              bfd_connections, &router_port->nbrp->header_,
++                              bfd_ports, &router_port->nbrp->header_,
+                               false, ROUTE_PRIO_OFFSET_CONNECTED,
+                               lrp->stateful_lflow_ref);
+                 }
+@@ -16739,7 +16908,7 @@ build_lrp_lflows_for_lbnats(struct ovn_port *op,
+ static void
+ build_lbnat_lflows_iterate_by_lrp(
+     struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
+-    const struct shash *meter_groups, const struct hmap *bfd_connections,
++    const struct shash *meter_groups, const struct sset *bfd_ports,
+     struct ds *match, struct ds *actions, struct lflow_table *lflows)
+ {
+     ovs_assert(op->nbrp);
+@@ -16752,7 +16921,7 @@ build_lbnat_lflows_iterate_by_lrp(
+     build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
+                                 actions, lflows);
+ 
+-    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_connections,
++    build_routable_flows_for_router_port(op, lr_stateful_rec, bfd_ports,
+                                          lflows, match, actions);
+ }
+ 
+@@ -16816,7 +16985,7 @@ struct lswitch_flow_build_info {
+     const struct shash *meter_groups;
+     const struct hmap *lb_dps_map;
+     const struct hmap *svc_monitor_map;
+-    const struct hmap *bfd_connections;
++    const struct sset *bfd_ports;
+     const struct chassis_features *features;
+     char *svc_check_match;
+     struct ds match;
+@@ -16827,7 +16996,6 @@ struct lswitch_flow_build_info {
+     struct hmap *parsed_routes;
+     struct hmap *route_policies;
+     struct simap *route_tables;
+-    struct simap *nexthops_table;
+ };
+ 
+ /* Helper function to combine all lflow generation which is iterated by
+@@ -16874,8 +17042,7 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
+     build_ip_routing_pre_flows_for_lrouter(od, lsi->lflows, NULL);
+     build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
+                                          lsi->parsed_routes, lsi->route_tables,
+-                                         lsi->bfd_connections, NULL,
+-                                         lsi->nexthops_table);
++                                         lsi->bfd_ports, NULL);
+     build_mcast_lookup_flows_for_lrouter(od, lsi->lflows, &lsi->match,
+                                          &lsi->actions, NULL);
+     build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
+@@ -16946,7 +17113,7 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
+                                           &lsi->actions, op->lflow_ref);
+     build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
+                                                 &lsi->actions, op->lflow_ref);
+-    build_ip_routing_flows_for_lrp(op, lsi->bfd_connections,
++    build_ip_routing_flows_for_lrp(op, lsi->bfd_ports,
+                                    lsi->lflows, op->lflow_ref);
+     build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
+                                        &lsi->actions, lsi->meter_groups,
+@@ -16965,10 +17132,13 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
+                                             lsi->meter_groups,
+                                             op->lflow_ref);
+     build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
+-                                lsi->meter_groups, lsi->bfd_connections,
++                                lsi->meter_groups, lsi->bfd_ports,
+                                 op->lflow_ref);
+     build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
+                                                  &lsi->actions, op->lflow_ref);
++    build_lrouter_routing_protocol_redirect(op, lsi->lflows, &lsi->match,
++                                            &lsi->actions, op->lflow_ref,
++                                            lsi->ls_ports);
+ }
+ 
+ static void *
+@@ -17055,7 +17225,7 @@ build_lflows_thread(void *arg)
+                     build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
+                     build_lbnat_lflows_iterate_by_lrp(
+                         op, lsi->lr_stateful_table, lsi->meter_groups,
+-                        lsi->bfd_connections, &lsi->match, &lsi->actions,
++                        lsi->bfd_ports, &lsi->match, &lsi->actions,
+                         lsi->lflows);
+                 }
+             }
+@@ -17196,14 +17366,13 @@ build_lswitch_and_lrouter_flows(
+     const struct shash *meter_groups,
+     const struct hmap *lb_dps_map,
+     const struct hmap *svc_monitor_map,
+-    const struct hmap *bfd_connections,
++    const struct sset *bfd_ports,
+     const struct chassis_features *features,
+     const char *svc_monitor_mac,
+     const struct sampling_app_table *sampling_apps,
+     struct hmap *parsed_routes,
+     struct hmap *route_policies,
+-    struct simap *route_tables,
+-    struct simap *nexthops_table)
++    struct simap *route_tables)
+ {
+ 
+     char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
+@@ -17232,7 +17401,7 @@ build_lswitch_and_lrouter_flows(
+             lsiv[index].meter_groups = meter_groups;
+             lsiv[index].lb_dps_map = lb_dps_map;
+             lsiv[index].svc_monitor_map = svc_monitor_map;
+-            lsiv[index].bfd_connections = bfd_connections;
++            lsiv[index].bfd_ports = bfd_ports;
+             lsiv[index].features = features;
+             lsiv[index].svc_check_match = svc_check_match;
+             lsiv[index].thread_lflow_counter = 0;
+@@ -17241,7 +17410,6 @@ build_lswitch_and_lrouter_flows(
+             lsiv[index].parsed_routes = parsed_routes;
+             lsiv[index].route_tables = route_tables;
+             lsiv[index].route_policies = route_policies;
+-            lsiv[index].nexthops_table = nexthops_table;
+             ds_init(&lsiv[index].match);
+             ds_init(&lsiv[index].actions);
+ 
+@@ -17278,7 +17446,7 @@ build_lswitch_and_lrouter_flows(
+             .meter_groups = meter_groups,
+             .lb_dps_map = lb_dps_map,
+             .svc_monitor_map = svc_monitor_map,
+-            .bfd_connections = bfd_connections,
++            .bfd_ports = bfd_ports,
+             .features = features,
+             .svc_check_match = svc_check_match,
+             .svc_monitor_mac = svc_monitor_mac,
+@@ -17288,7 +17456,6 @@ build_lswitch_and_lrouter_flows(
+             .route_policies = route_policies,
+             .match = DS_EMPTY_INITIALIZER,
+             .actions = DS_EMPTY_INITIALIZER,
+-            .nexthops_table = nexthops_table,
+         };
+ 
+         /* Combined build - all lflow generation from lswitch and lrouter
+@@ -17318,7 +17485,7 @@ build_lswitch_and_lrouter_flows(
+             build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
+             build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
+                                               lsi.meter_groups,
+-                                              lsi.bfd_connections,
++                                              lsi.bfd_ports,
+                                               &lsi.match,
+                                               &lsi.actions,
+                                               lsi.lflows);
+@@ -17449,14 +17616,13 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
+                                     input_data->meter_groups,
+                                     input_data->lb_datapaths_map,
+                                     input_data->svc_monitor_map,
+-                                    input_data->bfd_connections,
++                                    input_data->bfd_ports,
+                                     input_data->features,
+                                     input_data->svc_monitor_mac,
+                                     input_data->sampling_apps,
+                                     input_data->parsed_routes,
+                                     input_data->route_policies,
+-                                    input_data->route_tables,
+-                                    input_data->nexthops_table);
++                                    input_data->route_tables);
+ 
+     if (parallelization_state == STATE_INIT_HASH_SIZES) {
+         parallelization_state = STATE_USE_PARALLELIZATION;
+@@ -17818,7 +17984,7 @@ lflow_handle_lr_stateful_changes(struct ovsdb_idl_txn *ovnsb_txn,
+             build_lbnat_lflows_iterate_by_lrp(op,
+                                               lflow_input->lr_stateful_table,
+                                               lflow_input->meter_groups,
+-                                              lflow_input->bfd_connections,
++                                              lflow_input->bfd_ports,
+                                               &match, &actions,
+                                               lflows);
+ 
+@@ -18409,7 +18575,8 @@ build_static_mac_binding_table(
+     struct hmap *lr_ports)
+ {
+     /* Cleanup SB Static_MAC_Binding entries which do not have corresponding
+-     * NB Static_MAC_Binding entries. */
++     * NB Static_MAC_Binding entries, and SB Static_MAC_Binding entries for
++     * which there is not a NB Logical_Router_Port of the same name. */
+     const struct nbrec_static_mac_binding *nb_smb;
+     const struct sbrec_static_mac_binding *sb_smb;
+     SBREC_STATIC_MAC_BINDING_TABLE_FOR_EACH_SAFE (sb_smb,
+@@ -18419,6 +18586,12 @@ build_static_mac_binding_table(
+                                                sb_smb->ip);
+         if (!nb_smb) {
+             sbrec_static_mac_binding_delete(sb_smb);
++            continue;
++        }
++
++        struct ovn_port *op = ovn_port_find(lr_ports, nb_smb->logical_port);
++        if (!op || !op->nbrp || !op->od || !op->od->sb) {
++            sbrec_static_mac_binding_delete(sb_smb);
+         }
+     }
+ 
+@@ -18554,9 +18727,9 @@ bfd_init(struct bfd_data *data)
+ }
+ 
+ void
+-ecmp_nexthop_init(struct ecmp_nexthop_data *data)
++bfd_sync_init(struct bfd_sync_data *data)
+ {
+-    simap_init(&data->nexthops);
++    sset_init(&data->bfd_ports);
+ }
+ 
+ void
+@@ -18615,6 +18788,12 @@ bfd_destroy(struct bfd_data *data)
+     __bfd_destroy(&data->bfd_connections);
+ }
+ 
++void
++bfd_sync_destroy(struct bfd_sync_data *data)
++{
++    sset_destroy(&data->bfd_ports);
++}
++
+ void
+ route_policies_destroy(struct route_policies_data *data)
+ {
+@@ -18640,12 +18819,6 @@ static_routes_destroy(struct static_routes_data *data)
+     __bfd_destroy(&data->bfd_active_connections);
+ }
+ 
+-void
+-ecmp_nexthop_destroy(struct ecmp_nexthop_data *data)
+-{
+-    simap_destroy(&data->nexthops);
+-}
+-
+ void
+ ovnnb_db_run(struct northd_input *input_data,
+              struct northd_data *data,
+diff --git a/northd/northd.h b/northd/northd.h
+index 6e0258ff4..8f76d642d 100644
+--- a/northd/northd.h
++++ b/northd/northd.h
+@@ -93,6 +93,13 @@ ovn_datapath_find_by_key(struct hmap *datapaths, uint32_t dp_key);
+ 
+ bool od_has_lb_vip(const struct ovn_datapath *od);
+ 
++/* List of routing and routing-related protocols which
++ * OVN is capable of redirecting from LRP to specific LSP. */
++enum redirected_routing_protcol_flag_type {
++    REDIRECT_BGP = (1 << 0),
++    REDIRECT_BFD = (1 << 1),
++};
++
+ struct tracked_ovn_ports {
+     /* tracked created ports.
+      * hmapx node data is 'struct ovn_port *' */
+@@ -188,8 +195,8 @@ struct bfd_data {
+     struct hmap bfd_connections;
+ };
+ 
+-struct ecmp_nexthop_data {
+-    struct simap nexthops;
++struct bfd_sync_data {
++    struct sset bfd_ports;
+ };
+ 
+ struct lr_nat_table;
+@@ -213,7 +220,7 @@ struct lflow_input {
+     const struct ls_stateful_table *ls_stateful_table;
+     const struct shash *meter_groups;
+     const struct hmap *lb_datapaths_map;
+-    const struct hmap *bfd_connections;
++    const struct sset *bfd_ports;
+     const struct chassis_features *features;
+     const struct hmap *svc_monitor_map;
+     bool ovn_internal_version_changed;
+@@ -222,7 +229,6 @@ struct lflow_input {
+     struct hmap *parsed_routes;
+     struct hmap *route_policies;
+     struct simap *route_tables;
+-    struct simap *nexthops_table;
+ };
+ 
+ extern int parallelization_state;
+@@ -727,7 +733,7 @@ void northd_indices_create(struct northd_data *data,
+ void route_policies_init(struct route_policies_data *);
+ void route_policies_destroy(struct route_policies_data *);
+ void build_parsed_routes(struct ovn_datapath *, const struct hmap *,
+-                         struct hmap *, struct hmap *, struct simap *,
++                         const struct hmap *, struct hmap *, struct simap *,
+                          struct hmap *);
+ uint32_t get_route_table_id(struct simap *, const char *);
+ void static_routes_init(struct static_routes_data *);
+@@ -736,11 +742,8 @@ void static_routes_destroy(struct static_routes_data *);
+ void bfd_init(struct bfd_data *);
+ void bfd_destroy(struct bfd_data *);
+ 
+-void build_ecmp_nexthop_table(struct ovsdb_idl_txn *,
+-                              struct hmap *, struct simap *,
+-                              const struct sbrec_ecmp_nexthop_table *);
+-void ecmp_nexthop_init(struct ecmp_nexthop_data *);
+-void ecmp_nexthop_destroy(struct ecmp_nexthop_data *);
++void bfd_sync_init(struct bfd_sync_data *);
++void bfd_sync_destroy(struct bfd_sync_data *);
+ 
+ struct lflow_table;
+ struct lr_stateful_tracked_data;
+@@ -783,7 +786,8 @@ void build_route_policies(struct ovn_datapath *, const struct hmap *,
+                           const struct hmap *, struct hmap *, struct hmap *);
+ void bfd_table_sync(struct ovsdb_idl_txn *, const struct nbrec_bfd_table *,
+                     const struct hmap *, const struct hmap *,
+-                    const struct hmap *, const struct hmap *, struct hmap *);
++                    const struct hmap *, const struct hmap *,
++                    struct sset *);
+ void build_bfd_map(const struct nbrec_bfd_table *,
+                    const struct sbrec_bfd_table *, struct hmap *);
+ void run_update_worker_pool(int n_threads);
+diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
+index 3abd5f75b..ef5cd0c8c 100644
+--- a/northd/ovn-northd.8.xml
++++ b/northd/ovn-northd.8.xml
+@@ -284,6 +284,32 @@
+         dropped in the next stage.
+       </li>
+ 
++      <li>
++        <p>
++          For each logical port that's defined as a target of routing protocol
++          redirecting (via <code>routing-protocol-redirect</code> option set on
++          Logical Router Port), a filter is set in place that disallows
++          following traffic exiting this port:
++        </p>
++        <ul>
++          <li>
++            ARP replies
++          </li>
++          <li>
++            IPv6 Neighbor Discovery - Router Advertisements
++          </li>
++          <li>
++            IPv6 Neighbor Discovery - Neighbor Advertisements
++          </li>
++        </ul>
++        <p>
++          Since this port shares IP and MAC addresses with the Logical Router
++          Port, we wan't to prevent duplicate replies and advertisements. This
++          is achieved by a rule with priority 80 that sets
++          <code>REGBIT_PORT_SEC_DROP" = 1; next;"</code>.
++        </p>
++      </li>
++
+       <li>
+         For each (enabled) vtep logical port, a priority 70 flow is added which
+         matches on all packets and applies the action
+@@ -2002,6 +2028,34 @@ output;
+         on the logical switch.
+       </li>
+ 
++      <li>
++        <p>
++          For any logical port that's defined as a target of routing protocol
++          redirecting (via <code>routing-protocol-redirect</code> option set on
++          Logical Router Port), we redirect the traffic related to protocols
++          specified in <code>routing-protocols</code> option. It's acoomplished
++          with following priority-100 flows:
++        </p>
++        <ul>
++          <li>
++            Flows that match Logical Router Port's IPs and destination port of
++            the routing daemon are redirected to this port to allow external
++            peers' connection to the daemon listening on this port.
++          </li>
++          <li>
++            Flows that match Logical Router Port's IPs and source port of
++            the routing daemon are redirected to this port to allow replies
++            from the peers.
++          </li>
++        </ul>
++        <p>
++          In addition to this, we add priority-100 rules that
++          <code>clone</code> ARP replies and IPv6 Neighbor Advertisements to
++          this port as well. These allow to build proper ARP/IPv6 neighbor
++          list on this port.
++        </p>
++      </li>
++
+       <li>
+         Priority-90 flows for transit switches that forward registered
+         IP multicast traffic to their corresponding multicast group , which
+@@ -4274,7 +4328,18 @@ next;
+ ip.ttl--;
+ flags.loopback = 1;
+ reg8[0..15] = <var>GID</var>;
+-select(reg8[16..31], <var>MID1</var>, <var>MID2</var>, ...);
++reg8[16..31] = select(<var>MID1</var>, <var>MID2</var>, ...);
++        </pre>
++        <p>
++          However, when there is only one route in an ECMP group, group actions
++          will be:
++        </p>
++
++        <pre>
++ip.ttl--;
++flags.loopback = 1;
++reg8[0..15] = <var>GID</var>;
++reg8[16..31] = <var>MID1</var>);
+         </pre>
+       </li>
+ 
+diff --git a/ovn-nb.xml b/ovn-nb.xml
+index bbda423a5..2836f58f5 100644
+--- a/ovn-nb.xml
++++ b/ovn-nb.xml
+@@ -3575,6 +3575,48 @@ or
+         </p>
+       </column>
+ 
++      <column name="options" key="routing-protocol-redirect"
++              type='{"type": "string"}'>
++        <p>
++          NOTE: this feature is experimental and may be subject to
++          removal/change in the future.
++        </p>
++        <p>
++          This option expects a name of a Logical Switch Port that's present
++          in the peer's Logical Switch. If set, it causes any traffic
++          that's destined for Logical Router Port's IP addresses (including
++          its IPv6 LLA) and the ports associated with routing protocols defined
++          ip <code>routing-protocols</code> option, to be redirected
++          to the specified Logical Switch Port.
++
++          This allows external routing daemons to be bound to a port in OVN's
++          Logical Switch and act as if they were listening on Logical Router
++          Port's IP addresses.
++        </p>
++      </column>
++
++      <column name="options" key="routing-protocols" type='{"type": "string"}'>
++        <p>
++          NOTE: this feature is experimental and may be subject to
++          removal/change in the future.
++        </p>
++        <p>
++          This option expects a comma-separated list of routing, and
++          routing-related protocols, whose control plane traffic will be
++          redirected to a port specified in
++          <code>routing-protocol-redirect</code> option. Currently supported
++          options are:
++        </p>
++        <ul>
++          <li>
++            <code>BGP</code> (forwards TCP port 179)
++          </li>
++          <li>
++            <code>BFD</code> (forwards UDP port 3784)
++          </li>
++        </ul>
++      </column>
++
+       <column name="options" key="gateway_mtu_bypass">
+         <p>
+           When configured, represents a match expression, in the same
+diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
+index 9d8e0ac46..73abf2c8d 100644
+--- a/ovn-sb.ovsschema
++++ b/ovn-sb.ovsschema
+@@ -1,7 +1,7 @@
+ {
+     "name": "OVN_Southbound",
+-    "version": "20.36.0",
+-    "cksum": "1845967275 32154",
++    "version": "20.37.0",
++    "cksum": "1950136776 31493",
+     "tables": {
+         "SB_Global": {
+             "columns": {
+@@ -610,20 +610,6 @@
+                                           "refTable": "Datapath_Binding"}}}},
+             "indexes": [["logical_port", "ip"]],
+             "isRoot": true},
+-        "ECMP_Nexthop": {
+-            "columns": {
+-                "nexthop": {"type": "string"},
+-                "id": {"type": {"key": {"type": "integer",
+-                                        "minInteger": 0,
+-                                        "maxInteger": 65535}}},
+-                "external_ids": {
+-                    "type": {"key": "string", "value": "string",
+-                             "min": 0, "max": "unlimited"}},
+-                "options": {
+-                    "type": {"key": "string", "value": "string",
+-                             "min": 0, "max": "unlimited"}}},
+-            "indexes": [["nexthop"]],
+-            "isRoot": true},
+         "Chassis_Template_Var": {
+             "columns": {
+                 "chassis": {"type": "string"},
+diff --git a/ovn-sb.xml b/ovn-sb.xml
+index de0bd636f..5285cae30 100644
+--- a/ovn-sb.xml
++++ b/ovn-sb.xml
+@@ -1146,6 +1146,7 @@
+       <ul>
+         <li><code>eth.bcast</code> expands to <code>eth.dst == ff:ff:ff:ff:ff:ff</code></li>
+         <li><code>eth.mcast</code> expands to <code>eth.dst[40]</code></li>
++        <li><code>eth.mcastv6</code> expands to <code>eth.dst[32..47] == 0x3333</code></li>
+         <li><code>vlan.present</code> expands to <code>vlan.tci[12]</code></li>
+         <li><code>ip4</code> expands to <code>eth.type == 0x800</code></li>
+         <li><code>ip4.src_mcast</code> expands to
+@@ -1161,8 +1162,10 @@
+         <li><code>ip.first_frag</code> expands to <code>ip.is_frag &amp;&amp; !ip.later_frag</code></li>
+         <li><code>arp</code> expands to <code>eth.type == 0x806</code></li>
+         <li><code>rarp</code> expands to <code>eth.type == 0x8035</code></li>
++        <li><code>ip6.mcast</code> expands to <code>eth.mcastv6 &amp;&amp; ip6.dst[120..127] == 0xff</code></li>
+         <li><code>nd</code> expands to <code>icmp6.type == {135, 136} &amp;&amp; icmp6.code == 0 &amp;&amp; ip.ttl == 255</code></li>
+         <li><code>nd_ns</code> expands to <code>icmp6.type == 135 &amp;&amp; icmp6.code == 0 &amp;&amp; ip.ttl == 255</code></li>
++        <li><code>nd_ns_mcast</code> expands to <code>ip6.mcast  &amp;&amp; icmp6.type == 135 &amp;&amp; icmp6.code == 0 &amp;&amp; ip.ttl == 255</code></li>
+         <li><code>nd_na</code> expands to <code>icmp6.type == 136 &amp;&amp; icmp6.code == 0 &amp;&amp; ip.ttl == 255</code></li>
+         <li><code>nd_rs</code> expands to <code>icmp6.type == 133 &amp;&amp;
+         icmp6.code == 0 &amp;&amp; ip.ttl == 255</code></li>
+@@ -1366,6 +1369,8 @@
+         </dd>
+ 
+         <dt><code>ct_next;</code></dt>
++        <dt><code>ct_next(dnat);</code></dt>
++        <dt><code>ct_next(snat);</code></dt>
+         <dd>
+           <p>
+             Apply connection tracking to the flow, initializing
+@@ -5175,35 +5180,4 @@ tcp.flags = RST;
+       The set of variable values for a given chassis.
+     </column>
+   </table>
+-
+-  <table name="ECMP_Nexthop">
+-    <p>
+-      Each record in this table represents an active ECMP route committed by
+-      <code>ovn-northd</code> to <code>ovs</code> connection-tracking table.
+-      <code>ECMP_Nexthop</code> table is used by <code>ovn-controller</code>
+-      to track active ct entries and to flush stale ones.
+-    </p>
+-    <column name="nexthop">
+-      <p>
+-        Nexthop IP address for this ECMP route. Nexthop IP address should
+-        be the IP address of a connected router port or the IP address of
+-        an external device used as nexthop for the given destination.
+-      </p>
+-    </column>
+-
+-    <column name="id">
+-      <p>
+-        Nexthop unique identifier. Nexthop ID is used to track active
+-        ecmp-symmetric-reply connections and flush stale ones.
+-      </p>
+-    </column>
+-
+-    <column name="options">
+-      Reserved for future use.
+-    </column>
+-
+-    <column name="external_ids">
+-      See <em>External IDs</em> at the beginning of this document.
+-    </column>
+-  </table>
+ </database>
+diff --git a/tests/multinode.at b/tests/multinode.at
+index a04ce7597..a0eb8fc67 100644
+--- a/tests/multinode.at
++++ b/tests/multinode.at
+@@ -1062,6 +1062,10 @@ check_fake_multinode_setup
+ # Delete the multinode NB and OVS resources before starting the test.
+ cleanup_multinode_resources
+ 
++m_as ovn-chassis-1 ip link del sw0p1-p
++m_as ovn-chassis-2 ip link del sw0p2-p
++m_as ovn-chassis-2 ip link del sw1p1-p
++
+ check multinode_nbctl ls-add sw0
+ check multinode_nbctl lsp-add sw0 sw0-port1
+ check multinode_nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:03 10.0.0.3 1000::3"
+diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
+index dcab5f2e9..a2e451880 100644
+--- a/tests/ovn-controller.at
++++ b/tests/ovn-controller.at
+@@ -3140,62 +3140,116 @@ ovn_start
+ 
+ check_ct_zone_min() {
+     min_val=$1
+-    OVS_WAIT_UNTIL([test $(ovn-appctl -t ovn-controller ct-zone-list | awk '{print $2}' | sort | head -n1) -ge ${min_val}])
++    OVS_WAIT_UNTIL([test $(ovn-appctl -t ovn-controller ct-zone-list | awk '{printf "%02d\n", $2}' | sort | head -n1) -ge ${min_val}])
+ }
+ 
+ check_ct_zone_max() {
+     max_val=$1
+-    AT_CHECK([test $(ovn-appctl -t ovn-controller ct-zone-list | awk '{print $2}' | sort | tail -n1) -le ${max_val}])
++    OVS_WAIT_UNTIL([test $(ovn-appctl -t ovn-controller ct-zone-list | awk '{printf "%02d\n", $2}' | sort | tail -n1) -le ${max_val}])
+ }
+ 
+ net_add n1
+ sim_add hv1
+ as hv1
+ check ovs-vsctl add-br br-phys
++check ovs-appctl vlog/disable-rate-limit
++check ovs-appctl vlog/set vconn:DBG
++
+ ovn_attach n1 br-phys 192.168.0.1
+ 
+ check ovn-appctl -t ovn-controller vlog/set dbg:ct_zone
+ 
+ check ovs-vsctl add-port br-int lsp0 \
+     -- set Interface lsp0 external-ids:iface-id=lsp0
++check ovs-vsctl add-port br-int lsp1 \
++    -- set Interface lsp1 external-ids:iface-id=lsp1
+ 
+ check ovn-nbctl lr-add lr
++check ovn-nbctl ls-add ls0
++check ovn-nbctl ls-add ls1
+ 
+-check ovn-nbctl ls-add ls
+-check ovn-nbctl lsp-add ls ls-lr
+-check ovn-nbctl lsp-set-type ls-lr router
+-check ovn-nbctl lsp-set-addresses ls-lr router
+-check ovn-nbctl lrp-add lr lr-ls 00:00:00:00:00:01 10.0.0.1
++check ovn-nbctl set logical_router lr options:chassis=hv1
++
++check ovn-nbctl lrp-add lr lr-ls0 00:00:00:00:ff:01 10.0.0.1/24
++check ovn-nbctl lsp-add ls0 ls0-lr
++check ovn-nbctl lsp-set-type ls0-lr router
++check ovn-nbctl lsp-set-addresses ls0-lr 00:00:00:00:ff:01
++check ovn-nbctl lsp-set-options ls0-lr router-port=lr-ls0
+ 
+-check ovn-nbctl lsp-add ls lsp0
++check ovn-nbctl lsp-add ls0 lsp0
+ check ovn-nbctl lsp-set-addresses lsp0 "00:00:00:00:00:02 10.0.0.2"
+ 
+-check ovn-nbctl lrp-add lr lrp-gw 01:00:00:00:00:01 172.16.0.1
+-check ovn-nbctl lrp-set-gateway-chassis lrp-gw hv1
++check ovn-nbctl lrp-add lr lr-ls1 00:00:00:00:ff:02 172.16.0.1/24
++check ovn-nbctl lsp-add ls1 ls1-lr
++check ovn-nbctl lsp-set-type ls1-lr router
++check ovn-nbctl lsp-set-addresses ls1-lr 00:00:00:00:ff:02
++check ovn-nbctl lsp-set-options ls1-lr router-port=lr-ls1
+ 
+-# check regular boundaries
++check ovn-nbctl lsp-add ls1 lsp1
++check ovn-nbctl lsp-set-addresses lsp1 "00:00:00:00:00:02 172.16.0.2"
++wait_for_ports_up
++check ovn-nbctl --wait=hv sync
++
++AS_BOX([Check regular boundaries])
+ check_ct_zone_min 1
+-check_ct_zone_max 10
++check_ct_zone_max 12
++
++AS_BOX([Increase boundaries])
++ovs-vsctl set Open_vSwitch . external_ids:ct-zone-range=\"10-30\"
+ 
+-# increase boundaries
+-ovs-vsctl set Open_vSwitch . external_ids:ct-zone-range=\"10-20\"
+ check_ct_zone_min 10
+-check_ct_zone_max 20
++check_ct_zone_max 22
+ 
+-# reset min boundary
+-ovs-vsctl set Open_vSwitch . external_ids:ct-zone-range=\"5-20\"
++AS_BOX([Reset min boundary])
++check ovs-vsctl set Open_vSwitch . external_ids:ct-zone-range=\"5-30\"
+ 
+-# add a new port to the switch
+-check ovs-vsctl add-port br-int lsp1 \
+-    -- set Interface lsp1 external-ids:iface-id=lsp1
+-check ovn-nbctl lsp-add ls lsp1
+-check ovn-nbctl lsp-set-addresses lsp1 "00:00:00:00:00:03 10.0.0.3"
++# Add a new port to the ls0 switch
++check ovs-vsctl add-port br-int lsp2 \
++    -- set Interface lsp2 external-ids:iface-id=lsp2
++check ovn-nbctl lsp-add ls0 lsp2
++check ovn-nbctl lsp-set-addresses lsp2 "00:00:00:00:00:03 10.0.0.3"
+ check_ct_zone_min 5
+-check_ct_zone_max 20
++check_ct_zone_max 22
+ 
+ check ovn-nbctl set logical_router lr options:snat-ct-zone=2
++wait_for_ports_up
++check ovn-nbctl --wait=hv sync
++
+ check_ct_zone_min 2
+-check_ct_zone_max 20
++check_ct_zone_max 22
++
++AS_BOX([Check LR snat requested zone 2])
++AT_CHECK([test $(ovn-appctl -t ovn-controller ct-zone-list | awk '/lr_snat/{print $2}') -eq 2])
++
++n_flush=$(grep -c -i flush hv1/ovs-vswitchd.log)
++check ovn-appctl -t ovn-controller exit --restart
++# Make sure ovn-controller stopped before restarting it
++OVS_WAIT_UNTIL([test "$(ovn-appctl -t ovn-controller debug/status)" != "running"])
++start_daemon ovn-controller --verbose="ct_zone:dbg"
++wait_for_ports_up
++check ovn-nbctl --wait=hv sync
++
++# Check we do not have unexpected ct-flush restarting ovn-controller
++AT_CHECK([test $(grep -c -i flush hv1/ovs-vswitchd.log) -eq ${n_flush}])
++
++AS_BOX([Check LR snat allowed requested zone 0])
++check ovn-nbctl set logical_router lr options:snat-ct-zone=0
++check ovn-nbctl --wait=hv sync
++
++check_ct_zone_min 0
++check_ct_zone_max 22
++AT_CHECK([test $(ovn-appctl -t ovn-controller ct-zone-list | awk '/lr_snat/{print $2}') -eq 0])
++
++n_flush=$(grep -c -i flush hv1/ovs-vswitchd.log)
++check ovn-appctl -t ovn-controller exit --restart
++# Make sure ovn-controller stopped before restarting it
++OVS_WAIT_UNTIL([test "$(ovn-appctl -t ovn-controller debug/status)" != "running"])
++start_daemon ovn-controller --verbose="ct_zone:dbg"
++wait_for_ports_up
++check ovn-nbctl --wait=hv sync
++
++# Check we do not have unexpected ct-flush restarting ovn-controller
++AT_CHECK([test $(grep -c -i flush hv1/ovs-vswitchd.log) -eq ${n_flush}])
+ 
+ OVN_CLEANUP([hv1])
+ AT_CLEANUP
+diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
+index 8497cb194..9fa41200e 100644
+--- a/tests/ovn-ic.at
++++ b/tests/ovn-ic.at
+@@ -173,7 +173,7 @@ done
+ create_ic_infra() {
+     az_id=$1
+     ts_id=$2
+-    az=az$i
++    az=az$1
+ 
+     lsp=lsp${az_id}-${ts_id}
+     lrp=lrp${az_id}-${ts_id}
+@@ -184,7 +184,7 @@ create_ic_infra() {
+ 
+     check ovn-ic-nbctl --wait=sb ts-add $ts
+     check ovn-nbctl lr-add $lr
+-    check ovn-nbctl lrp-add $lr $lrp 00:00:00:00:00:0$az_id 10.0.$az_id.1/24
++    check ovn-nbctl --wait=sb lrp-add $lr $lrp 00:00:00:00:00:0$az_id 10.0.$az_id.1/24
+     check ovn-nbctl lrp-set-gateway-chassis $lrp gw-$az
+ 
+     check ovn-nbctl lsp-add $ts $lsp -- \
+@@ -192,7 +192,7 @@ create_ic_infra() {
+         lsp-set-type $lsp router -- \
+         lsp-set-options $lsp router-port=$lrp
+ 
+-    check ovn-nbctl lr-route-add $lr 192.168.0.0/16 10.0.$az_id.10
++    check ovn-nbctl --wait=sb lr-route-add $lr 192.168.0.0/16 10.0.$az_id.10
+ }
+ 
+ create_ic_infra 1 1
+@@ -209,7 +209,7 @@ check_row_count ic-sb:Route 3 ip_prefix=192.168.0.0/16
+ check ovn-ic-nbctl --wait=sb ts-del ts1-1
+ ovn-ic-sbctl list route
+ ovn-ic-nbctl list transit_switch
+-checl_row_count ic-sb:route 2 ip_prefix=192.168.0.0/16
++check_row_count ic-sb:route 2 ip_prefix=192.168.0.0/16
+ ovn-ic-sbctl list route
+ 
+ for i in 1 2; do
+@@ -255,7 +255,7 @@ for i in 1 2; do
+     check ovn-nbctl lrp-add lr1 lrp$i 00:00:00:00:0$i:01 10.0.$i.1/24
+     check ovn-nbctl lrp-set-gateway-chassis lrp$i gw-az$i
+ 
+-    check ovn-nbctl lsp-add ts1 lsp$i -- \
++    check ovn-nbctl --wait=sb lsp-add ts1 lsp$i -- \
+         lsp-set-addresses lsp$i router -- \
+         lsp-set-type lsp$i router -- \
+         lsp-set-options lsp$i router-port=lrp$i
+@@ -263,13 +263,11 @@ done
+ 
+ ovn_as az1
+ 
+-ovn-nbctl \
+-  --id=@id create logical-router-static-route ip_prefix=1.1.1.1/32 nexthop=10.0.1.10 -- \
+-  add logical-router lr1 static_routes @id
+ ovn-nbctl \
+   --id=@id create logical-router-static-route ip_prefix=1.1.1.1/32 nexthop=10.0.1.10 -- \
+   add logical-router lr1 static_routes @id
+ 
++check ovn-ic-nbctl --wait=sb sync
+ check ovn-ic-nbctl --wait=sb sync
+ check_row_count ic-sb:route 1 ip_prefix=1.1.1.1/32
+ 
+@@ -455,6 +453,7 @@ Route Table <main>:
+ # Delete route in AZ1, AZ2's learned route should be deleted.
+ ovn_as az1 ovn-nbctl lr-route-del lr1 10.11.1.0/24
+ ovn-ic-nbctl --wait=sb sync
++ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep -c learned], [1], [dnl
+ 0
+ ])
+@@ -462,6 +461,7 @@ AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep -c learned], [1], [dnl
+ # Add the route back
+ ovn_as az1 ovn-nbctl lr-route-add lr1 10.11.1.0/24 169.254.0.1
+ ovn-ic-nbctl --wait=sb sync
++ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2 | grep -c learned], [0], [dnl
+ 1
+ ])
+@@ -485,6 +485,7 @@ ovn_as az1 ovn-nbctl set nb_global . options:ic-route-adv=false
+ # AZ2 shouldn't have the route learned, because AZ1 should have stopped
+ # advertising.
+ check ovn-ic-nbctl --wait=sb sync
++check ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2], [0], [dnl
+ IPv4 Routes
+ Route Table <main>:
+@@ -499,6 +500,7 @@ ovn_as az1 ovn-nbctl lr-route-add lr1 0.0.0.0/0 169.254.0.3
+ ovn_as az1 ovn-nbctl set nb_global . options:ic-route-adv=true
+ ovn_as az1 ovn-nbctl set nb_global . options:ic-route-learn=true
+ check ovn-ic-nbctl --wait=sb sync
++check ovn-ic-nbctl --wait=sb sync
+ 
+ # Default route should NOT get advertised or learned, by default.
+ AT_CHECK([ovn_as az2 ovn-nbctl lr-route-list lr2], [0], [dnl
+@@ -576,7 +578,7 @@ for i in 1 2; do
+ done
+ 
+ # Create directly-connected routes
+-ovn_as az2 ovn-nbctl lrp-add lr12 lrp-lr12-ls2 aa:aa:aa:aa:bb:01 "192.168.0.1/24"
++ovn_as az2 ovn-nbctl --wait=sb lrp-add lr12 lrp-lr12-ls2 aa:aa:aa:aa:bb:01 "192.168.0.1/24"
+ ovn_as az2 ovn-nbctl lr-route-add lr12 10.10.10.0/24 192.168.0.10
+ ovn_as az1 ovn-nbctl --wait=sb sync
+ 
+@@ -585,6 +587,7 @@ ovn_as az1 ovn-nbctl show
+ echo az2
+ ovn_as az2 ovn-nbctl show
+ check ovn-ic-nbctl --wait=sb sync
++check ovn-ic-nbctl --wait=sb sync
+ 
+ # Test routes from lr12 were learned to lr11
+ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 |
+@@ -626,7 +629,7 @@ for i in 1 2; do
+     ovn-nbctl lrp-add lr$i lrp-lr$i-p$i 00:00:00:00:00:0$i 192.168.$i.1/24
+ 
+     # Create static routes
+-    ovn-nbctl lr-route-add lr$i 10.11.$i.0/24 169.254.0.1
++    ovn-nbctl --wait=sb lr-route-add lr$i 10.11.$i.0/24 169.254.0.1
+ 
+     # Create a src-ip route, which shouldn't be synced
+     ovn-nbctl --policy=src-ip lr-route-add lr$i 10.22.$i.0/24 169.254.0.2
+@@ -665,7 +668,6 @@ ovn-ic-nbctl ts-add ts1
+ for i in 1 2; do
+     ovn_start az$i
+     ovn_as az$i
+-
+     # Enable route learning at AZ level
+     ovn-nbctl set nb_global . options:ic-route-learn=true
+     # Enable route advertising at AZ level
+@@ -680,9 +682,10 @@ for i in 1 2; do
+             -- lsp-set-type lsp-ts1-lr$i router \
+             -- lsp-set-options lsp-ts1-lr$i router-port=lrp-lr$i-ts1
+ 
+-    ovn-nbctl lrp-add lr$i lrp-lr$i-p$i 00:00:00:00:00:0$i 2002:db8:1::$i/64
++    ovn-nbctl --wait=sb lrp-add lr$i lrp-lr$i-p$i 00:00:00:00:00:0$i 2002:db8:1::$i/64
+ done
+ 
++check ovn-ic-nbctl --wait=sb sync
+ check ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1 | awk '/learned/{print $1, $2}'], [0], [dnl
+ 2002:db8:1::/64 2001:db8:1::2
+@@ -733,6 +736,7 @@ for i in 1 2; do
+     ovn-nbctl --policy=src-ip --route-table=rtb1 lr-route-add lr$i 10.22.$i.0/24 169.254.0.2
+ done
+ 
++check ovn-ic-nbctl --wait=sb sync
+ check ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1], [0], [dnl
+ IPv4 Routes
+@@ -750,6 +754,7 @@ for i in 1 2; do
+     ovn_as az$i ovn-nbctl --route-table=rtb1 lr-route-add lr$i 10.11.$i.0/24 169.254.0.1
+ done
+ 
++check ovn-ic-nbctl --wait=sb sync
+ check ovn-ic-nbctl --wait=sb sync
+ # ensure route from rtb1 is not learned to any route table as route table is
+ # not set to TS port
+@@ -975,6 +980,7 @@ ovn_as az2 ovn-nbctl --route-table=rtb3 lr-route-add lr12 10.10.10.0/24 192.168.
+ # Create directly-connected route in VPC2
+ ovn_as az2 ovn-nbctl --wait=sb lrp-add lr22 lrp-lr22 aa:aa:aa:aa:bb:01 "192.168.0.1/24"
+ check ovn-ic-nbctl --wait=sb sync
++check ovn-ic-nbctl --wait=sb sync
+ # Test direct routes from lr12 were learned to lr11
+ OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 192.168 |
+              grep learned | awk '{print $1, $2, $5}' | sort ], [0], [dnl
+@@ -1102,6 +1108,10 @@ ovn_as az2 ovn-nbctl --route-table=rtb3 lr-route-add lr12 2001:db8:aaaa::/64 200
+ ovn_as az2 ovn-nbctl --wait=sb lrp-add lr22 lrp-lr22 aa:aa:aa:aa:bb:01 "2001:db8:200::1/64"
+ 
+ # Test direct routes from lr12 were learned to lr11
++#
++# We need to wait twice: first time for az2/ic to handle port addition and update ic/sb and
++# second time for az1/ic to handle ic/sb update.
++check ovn-ic-nbctl --wait=sb sync
+ check ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 2001:db8:200 |
+              grep learned | awk '{print $1, $2, $5}' | sort], [0], [dnl
+@@ -1177,6 +1187,7 @@ for i in 1 2; do
+     check ovn-nbctl --wait=sb lr-route-add $lr 0.0.0.0/0 192.168.$i.11
+ done
+ 
++check ovn-ic-nbctl --wait=sb sync
+ check ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep dst-ip | sort] , [0], [dnl
+                 0.0.0.0/0              192.168.1.11 dst-ip
+@@ -1249,14 +1260,14 @@ for i in 1 2; do
+             -- lsp-set-options $lsp router-port=$lrp
+ done
+ 
+-
+ # Create directly-connected routes
+ ovn_as az1 ovn-nbctl lrp-add lr11 lrp-lr11 aa:aa:aa:aa:bb:01 "192.168.0.1/24"
+ ovn_as az2 ovn-nbctl lrp-add lr21 lrp-lr21 aa:aa:aa:aa:bc:01 "192.168.1.1/24"
+-ovn_as az2 ovn-nbctl lrp-add lr22 lrp-lr22 aa:aa:aa:aa:bc:02 "192.168.2.1/24"
++ovn_as az2 ovn-nbctl --wait=sb lrp-add lr22 lrp-lr22 aa:aa:aa:aa:bc:02 "192.168.2.1/24"
+ 
+ # Test direct routes from lr21 and lr22 were learned to lr11
+ check ovn-ic-nbctl --wait=sb sync
++check ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 192.168 |
+              grep learned | awk '{print $1, $2}' | sort ], [0], [dnl
+ 192.168.1.0/24 169.254.10.21
+@@ -1335,7 +1346,6 @@ check ovn-ic-nbctl ts-add ts1
+ for i in 1 2; do
+     ovn_start az$i
+     ovn_as az$i
+-
+     # Enable route learning at AZ level
+     check ovn-nbctl set nb_global . options:ic-route-learn=true
+     # Enable route advertising at AZ level
+@@ -1369,10 +1379,11 @@ for i in 1 2; do
+             33:33:33:33:33:3$i 2005:1734:5678::$i/50
+ 
+     # additional not filtered prefix -> different subnet bits
+-    check ovn-nbctl lrp-add lr$i lrp-lr$i-p-ext4$i \
++    check ovn-nbctl --wait=sb lrp-add lr$i lrp-lr$i-p-ext4$i \
+             44:44:44:44:44:4$i 2005:1834:5678::$i/50
+ done
+ 
++check ovn-ic-nbctl --wait=sb sync
+ check ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1 |
+     awk '/learned/{print $1, $2}' ], [0], [dnl
+@@ -1387,6 +1398,7 @@ for i in 1 2; do
+     check ovn-nbctl remove nb_global . options ic-route-denylist
+ done
+ 
++check ovn-ic-nbctl --wait=sb sync
+ check ovn-ic-nbctl --wait=sb sync
+ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr1 |
+     awk '/learned/{print $1, $2}' | sort ], [0], [dnl
+@@ -1750,6 +1762,7 @@ check ovn-nbctl lsp-add ts ts-lr3 \
+ 
+ wait_for_ports_up
+ check ovn-ic-nbctl --wait=sb sync
++check ovn-ic-nbctl --wait=sb sync
+ 
+ ovn_as az1
+ check ovn-nbctl lsp-set-options ts-lr2 requested-chassis=hv2
+@@ -1970,6 +1983,7 @@ check ovn-nbctl lsp-add ts ts-lr3 \
+ 
+ wait_for_ports_up
+ check ovn-ic-nbctl --wait=sb sync
++check ovn-ic-nbctl --wait=sb sync
+ ovn_as az1
+ check ovn-nbctl lsp-set-options ts-lr2 requested-chassis=hv2
+ check ovn-nbctl lsp-set-options ts-lr3 requested-chassis=hv2
+@@ -2223,6 +2237,7 @@ check ovn-nbctl lsp-add ts ts-lr3 \
+ 
+ wait_for_ports_up
+ check ovn-ic-nbctl --wait=sb sync
++check ovn-ic-nbctl --wait=sb sync
+ ovn_as az1
+ check ovn-nbctl lsp-set-options ts-lr2 requested-chassis=hv2
+ check ovn-nbctl lsp-set-options ts-lr3 requested-chassis=hv2
+diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
+index e4c882265..d6a8c4640 100644
+--- a/tests/ovn-northd.at
++++ b/tests/ovn-northd.at
+@@ -1133,7 +1133,8 @@ ovn_start
+ # DR is connected to S1 and CR is connected to S2
+ 
+ check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 \
+-  -- set chassis gw1 other_config:ct-commit-to-zone="true"
++  -- set chassis gw1 other_config:ct-commit-to-zone="true" \
++  -- set chassis gw1 other_config:ct-next-zone="true"
+ 
+ check ovn-nbctl lr-add DR
+ check ovn-nbctl lrp-add DR DR-S1 02:ac:10:01:00:01 172.16.1.1/24
+@@ -5721,7 +5722,8 @@ AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
+ ])
+ 
+ check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 \
+-  -- set chassis gw1 other_config:ct-commit-to-zone="true"
++  -- set chassis gw1 other_config:ct-commit-to-zone="true" \
++  -- set chassis gw1 other_config:ct-next-zone="true"
+ 
+ # Create a distributed gw port on lr0
+ check ovn-nbctl ls-add public
+@@ -5822,8 +5824,8 @@ AT_CHECK([grep "lr_out_undnat" lr0flows | ovn_strip_lflows], [0], [dnl
+ 
+ AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl
+   table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
+-  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
+-  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src == 10.0.0.10 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
++  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_next(snat);)
++  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src == 10.0.0.10 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_next(snat);)
+ ])
+ 
+ AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
+@@ -5980,8 +5982,8 @@ AT_CHECK([grep "lr_out_undnat" lr0flows | ovn_strip_lflows], [0], [dnl
+ 
+ AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0], [dnl
+   table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
+-  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
+-  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src == 10.0.0.10 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
++  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src == 10.0.0.0/24 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_next(snat);)
++  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src == 10.0.0.10 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)), action=(ct_next(snat);)
+ ])
+ 
+ AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
+@@ -6799,26 +6801,27 @@ check ovn-nbctl lsp-set-type public-lr0 router
+ check ovn-nbctl lsp-set-addresses public-lr0 router
+ check ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
+ 
++# ECMP flows will be added even if there is only one next-hop.
+ check ovn-nbctl --wait=sb --ecmp-symmetric-reply lr-route-add lr0 1.0.0.1 192.168.0.10
+-check_row_count ECMP_Nexthop 1
+ 
+ ovn-sbctl dump-flows lr0 > lr0flows
+ 
+ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
+   table=??(lr_in_ip_routing   ), priority=0    , match=(1), action=(drop;)
++  table=??(lr_in_ip_routing   ), priority=10300, match=(ct_mark.ecmp_reply_port == 1 && reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; eth.src = 00:00:20:20:12:13; reg1 = 192.168.0.1; outport = "lr0-public"; next;)
+   table=??(lr_in_ip_routing   ), priority=10550, match=(nd_rs || nd_ra), action=(drop;)
+   table=??(lr_in_ip_routing   ), priority=194  , match=(inport == "lr0-public" && ip6.dst == fe80::/64), action=(ip.ttl--; reg8[[0..15]] = 0; xxreg0 = ip6.dst; xxreg1 = fe80::200:20ff:fe20:1213; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;)
+   table=??(lr_in_ip_routing   ), priority=74   , match=(ip4.dst == 192.168.0.0/24), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = ip4.dst; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;)
+-  table=??(lr_in_ip_routing   ), priority=97   , match=(reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; reg8[[0..15]] = 0; reg0 = 192.168.0.10; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; flags.loopback = 1; next;)
++  table=??(lr_in_ip_routing   ), priority=97   , match=(reg7 == 0 && ip4.dst == 1.0.0.1/32), action=(ip.ttl--; flags.loopback = 1; reg8[[0..15]] = 1; reg8[[16..31]] = 1; next;)
+ ])
+ 
+ AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | ovn_strip_lflows], [0], [dnl
+   table=??(lr_in_ip_routing_ecmp), priority=0    , match=(1), action=(drop;)
++  table=??(lr_in_ip_routing_ecmp), priority=100  , match=(reg8[[0..15]] == 1 && reg8[[16..31]] == 1), action=(reg0 = 192.168.0.10; reg1 = 192.168.0.1; eth.src = 00:00:20:20:12:13; outport = "lr0-public"; next;)
+   table=??(lr_in_ip_routing_ecmp), priority=150  , match=(reg8[[0..15]] == 0), action=(next;)
+ ])
+ 
+ check ovn-nbctl --wait=sb --ecmp-symmetric-reply lr-route-add lr0 1.0.0.1 192.168.0.20
+-check_row_count ECMP_Nexthop 2
+ 
+ ovn-sbctl dump-flows lr0 > lr0flows
+ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
+@@ -6848,7 +6851,6 @@ AT_CHECK([grep -e "lr_in_arp_resolve.*ecmp" lr0flows | ovn_strip_lflows], [0], [
+ 
+ # add ecmp route with wrong nexthop
+ check ovn-nbctl --wait=sb --ecmp-symmetric-reply lr-route-add lr0 1.0.0.1 192.168.1.20
+-check_row_count ECMP_Nexthop 2
+ 
+ ovn-sbctl dump-flows lr0 > lr0flows
+ AT_CHECK([grep -w "lr_in_ip_routing" lr0flows | ovn_strip_lflows], [0], [dnl
+@@ -6868,7 +6870,6 @@ AT_CHECK([grep -e "lr_in_ip_routing_ecmp" lr0flows | sed 's/192\.168\.0\..0/192.
+ 
+ check ovn-nbctl lr-route-del lr0
+ wait_row_count nb:Logical_Router_Static_Route 0
+-check_row_count ECMP_Nexthop 0
+ 
+ check ovn-nbctl --wait=sb lr-route-add lr0 1.0.0.0/24 192.168.0.10
+ ovn-sbctl dump-flows lr0 > lr0flows
+@@ -7876,13 +7877,16 @@ ovn_start
+ # distributed gateway LRPs.
+ 
+ check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 \
+-  -- set chassis gw1 other_config:ct-commit-to-zone="true"
++  -- set chassis gw1 other_config:ct-commit-to-zone="true" \
++  -- set chassis gw1 other_config:ct-next-zone="true"
+ 
+ check ovn-sbctl chassis-add gw2 geneve 128.0.0.1 \
+-  -- set chassis gw2 other_config:ct-commit-to-zone="true"
++  -- set chassis gw2 other_config:ct-commit-to-zone="true" \
++  -- set chassis gw2 other_config:ct-next-zone="true"
+ 
+ check ovn-sbctl chassis-add gw3 geneve 129.0.0.1 \
+-  -- set chassis gw3 other_config:ct-commit-to-zone="true"
++  -- set chassis gw3 other_config:ct-commit-to-zone="true" \
++  -- set chassis gw3 other_config:ct-next-zone="true"
+ 
+ check ovn-nbctl lr-add DR
+ check ovn-nbctl lrp-add DR DR-S1 02:ac:10:01:00:01 172.16.1.1/24
+@@ -8084,6 +8088,19 @@ wait_row_count Static_MAC_Binding 1 logical_port=lr0-p0 ip=192.168.10.100 mac="0
+ AT_CLEANUP
+ ])
+ 
++OVN_FOR_EACH_NORTHD_NO_HV([
++AT_SETUP([LR NB Static_MAC_Binding garbage collection])
++ovn_start
++
++check ovn-nbctl lr-add lr -- lrp-add lr lr-lrp 00:00:00:00:00:01 1.1.1.1/24
++check ovn-nbctl static-mac-binding-add lr-lrp 1.1.1.2 00:00:00:00:00:02
++wait_row_count sb:Static_MAC_Binding 1 logical_port=lr-lrp ip=1.1.1.2 mac="00\:00\:00\:00\:00\:02"
++check ovn-nbctl lr-del lr
++wait_row_count sb:Static_MAC_Binding 0
++
++AT_CLEANUP
++])
++
+ OVN_FOR_EACH_NORTHD_NO_HV([
+ AT_SETUP([LR neighbor lookup and learning flows])
+ ovn_start
+@@ -9490,9 +9507,9 @@ AT_CAPTURE_FILE([S1flows])
+ AT_CHECK([grep -e "ls_in_arp_rsp" S1flows | ovn_strip_lflows], [0], [dnl
+   table=??(ls_in_arp_rsp      ), priority=0    , match=(1), action=(next;)
+   table=??(ls_in_arp_rsp      ), priority=100  , match=(arp.tpa == 192.168.0.10 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff && inport == "S1-vm1"), action=(next;)
+-  table=??(ls_in_arp_rsp      ), priority=100  , match=(nd_ns && ip6.dst == {fd00::10, ff02::1:ff00:10} && nd.target == fd00::10 && inport == "S1-vm1"), action=(next;)
++  table=??(ls_in_arp_rsp      ), priority=100  , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:10 && nd.target == fd00::10 && inport == "S1-vm1"), action=(next;)
+   table=??(ls_in_arp_rsp      ), priority=50   , match=(arp.tpa == 192.168.0.10 && arp.op == 1 && eth.dst == ff:ff:ff:ff:ff:ff), action=(eth.dst = eth.src; eth.src = 50:54:00:00:00:10; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 50:54:00:00:00:10; arp.tpa = arp.spa; arp.spa = 192.168.0.10; outport = inport; flags.loopback = 1; output;)
+-  table=??(ls_in_arp_rsp      ), priority=50   , match=(nd_ns && ip6.dst == {fd00::10, ff02::1:ff00:10} && nd.target == fd00::10), action=(nd_na { eth.src = 50:54:00:00:00:10; ip6.src = fd00::10; nd.target = fd00::10; nd.tll = 50:54:00:00:00:10; outport = inport; flags.loopback = 1; output; };)
++  table=??(ls_in_arp_rsp      ), priority=50   , match=(nd_ns_mcast && ip6.dst == ff02::1:ff00:10 && nd.target == fd00::10), action=(nd_na { eth.src = 50:54:00:00:00:10; ip6.src = fd00::10; nd.target = fd00::10; nd.tll = 50:54:00:00:00:10; outport = inport; flags.loopback = 1; output; };)
+ ])
+ 
+ #Set the disable_arp_nd_rsp option and verify the flow
+@@ -12615,6 +12632,7 @@ AT_SETUP([Sampling_App incremental processing])
+ 
+ ovn_start
+ 
++ovn-nbctl --wait=sb sync
+ check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
+ 
+ ovn-nbctl create Sampling_App type="acl-new" id="42"
+@@ -13761,3 +13779,96 @@ AT_CHECK([grep -e "172.168.0.110" -e "172.168.0.120" -e "10.0.0.3" -e "20.0.0.3"
+ 
+ AT_CLEANUP
+ ])
++
++OVN_FOR_EACH_NORTHD_NO_HV([
++AT_SETUP([Routing protocol control plane redirect])
++ovn_start
++
++check ovn-sbctl chassis-add hv1 geneve 127.0.0.1
++
++check ovn-nbctl lr-add lr -- \
++    lrp-add lr lr-ls 02:ac:10:01:00:01 172.16.1.1/24
++check ovn-nbctl --wait=sb set logical_router lr options:chassis=hv1
++
++check ovn-nbctl ls-add ls -- \
++    lsp-add ls ls-lr -- \
++    lsp-set-type ls-lr router -- \
++    lsp-set-addresses ls-lr router -- \
++    lsp-set-options ls-lr router-port=lr-ls
++
++check ovn-nbctl lsp-add ls lsp-bgp -- \
++    lsp-set-addresses lsp-bgp unknown
++
++# Function that ensures that no redirect rules are installed.
++check_no_redirect() {
++    AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup  | grep -E "tcp.dst == 179|tcp.src == 179" | wc -l], [0], [0
++])
++
++    AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_check_port_sec | grep -E "priority=80" | wc -l], [0], [0
++])
++    check_no_bfd_redirect
++}
++
++# Function that ensures that no BFD redirect rules are installed.
++check_no_bfd_redirect() {
++    AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup  | grep -E "udp.dst == 3784|udp.src == 3784" | wc -l], [0], [0
++])
++}
++
++# By default, no rules related to routing protocol redirect are present
++check_no_redirect
++
++# Set "lsp-bgp" port as target of BGP control plane redirected traffic
++check ovn-nbctl --wait=sb set logical_router_port lr-ls options:routing-protocol-redirect=lsp-bgp
++check ovn-nbctl --wait=sb set logical_router_port lr-ls options:routing-protocols=BGP
++
++# Check that BGP control plane traffic is redirected "lsp-bgp"
++AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup | grep -E "tcp.dst == 179|tcp.src == 179" | ovn_strip_lflows], [0], [dnl
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(ip4.dst == 172.16.1.1 && tcp.dst == 179), action=(outport = "lsp-bgp"; output;)
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(ip4.dst == 172.16.1.1 && tcp.src == 179), action=(outport = "lsp-bgp"; output;)
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(ip6.dst == fe80::ac:10ff:fe01:1 && tcp.dst == 179), action=(outport = "lsp-bgp"; output;)
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(ip6.dst == fe80::ac:10ff:fe01:1 && tcp.src == 179), action=(outport = "lsp-bgp"; output;)
++])
++
++# Check that ARP/ND traffic is cloned to the "lsp-bgp"
++AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup | grep "arp.op == 2 && arp.tpa == 172.16.1.1" | ovn_strip_lflows], [0], [dnl
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(arp.op == 2 && arp.tpa == 172.16.1.1), action=(clone { outport = "lsp-bgp"; output; }; outport = "ls-lr"; output;)
++])
++AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup | grep "&& nd_na" | ovn_strip_lflows], [0], [dnl
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(ip6.dst == fe80::ac:10ff:fe01:1 && nd_na), action=(clone { outport = "lsp-bgp"; output; }; outport = "ls-lr"; output;)
++])
++
++# Check that at this point no BFD redirecting is present
++check_no_bfd_redirect
++
++# Add BFD traffic redirect
++check ovn-nbctl --wait=sb set logical_router_port lr-ls options:routing-protocols=BGP,BFD
++
++# Check that BFD traffic is redirected to "lsp-bgp"
++AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup | grep -E "udp.dst == 3784|udp.src == 3784" | ovn_strip_lflows], [0], [dnl
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(ip4.dst == 172.16.1.1 && udp.dst == 3784), action=(outport = "lsp-bgp"; output;)
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(ip4.dst == 172.16.1.1 && udp.src == 3784), action=(outport = "lsp-bgp"; output;)
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(ip6.dst == fe80::ac:10ff:fe01:1 && udp.dst == 3784), action=(outport = "lsp-bgp"; output;)
++  table=??(ls_in_l2_lkup      ), priority=100  , match=(ip6.dst == fe80::ac:10ff:fe01:1 && udp.src == 3784), action=(outport = "lsp-bgp"; output;)
++])
++
++
++# Check that ARP replies and ND advertisements are blocked from exiting "lsp-bgp"
++AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_check_port_sec | grep "priority=80" | ovn_strip_lflows], [0], [dnl
++  table=??(ls_in_check_port_sec), priority=80   , match=(inport == "lsp-bgp" && arp.op == 2), action=(reg0[[15]] = 1; next;)
++  table=??(ls_in_check_port_sec), priority=80   , match=(inport == "lsp-bgp" && nd_na), action=(reg0[[15]] = 1; next;)
++  table=??(ls_in_check_port_sec), priority=80   , match=(inport == "lsp-bgp" && nd_ra), action=(reg0[[15]] = 1; next;)
++])
++
++# Remove 'bgp-redirect' option from LRP and check that rules are removed
++check ovn-nbctl --wait=sb remove logical_router_port lr-ls options routing-protocol-redirect
++check ovn-nbctl --wait=sb remove logical_router_port lr-ls options routing-protocols
++check_no_redirect
++
++# Set non-existent LSP as target of 'bgp-redirect' and check that no rules are added
++check ovn-nbctl --wait=sb set logical_router_port lr-ls options:routing-protocol-redirect=lsp-foo
++check ovn-nbctl --wait=sb set logical_router_port lr-ls options:routing-protocols=BGP,BFD
++check_no_redirect
++
++AT_CLEANUP
++])
+diff --git a/tests/ovn.at b/tests/ovn.at
+index a1d689e84..ec6e6c100 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -1263,11 +1263,27 @@ ct_lb_mark(backends=192.168.1.2:80,192.168.1.3:80; hash_fields="eth_src,eth_dst,
+ 
+ # ct_next
+ ct_next;
++    formats as ct_next(dnat);
++    encodes as ct(table=oflow_in_table,zone=NXM_NX_REG13[[0..15]])
++    has prereqs ip
++ct_next(dnat);
++    encodes as ct(table=oflow_in_table,zone=NXM_NX_REG13[[0..15]])
++    has prereqs ip
++ct_next(snat);
+     encodes as ct(table=oflow_in_table,zone=NXM_NX_REG13[[0..15]])
+     has prereqs ip
+ ct_clear; ct_next;
++    formats as ct_clear; ct_next(dnat);
+     encodes as ct_clear,ct(table=oflow_in_table,zone=NXM_NX_REG13[[0..15]])
+     has prereqs ip
++ct_next(snat, dnat);
++    Syntax error at `,' expecting `)'.
++ct_next(dnat, ignore);
++    Syntax error at `,' expecting `)'.
++ct_next(ignore);
++    "ct_next" action accepts only "dnat" or "snat" parameter.
++ct_next();
++    "ct_next" action accepts only "dnat" or "snat" parameter.
+ 
+ # ct_commit
+ ct_commit;
+@@ -8617,7 +8633,7 @@ done
+ # Complete Neighbor Solicitation packet and Neighbor Advertisement packet
+ # vif1 -> NS -> vif2.  vif1 <- NA <- ovn-controller.
+ # vif2 will not receive NS packet, since ovn-controller will reply for it.
+-ns_packet=3333ffa1f9aefa163e94059886dd6000000000203afffd81ce49a9480000f8163efffe940598fd81ce49a9480000f8163efffea1f9ae8700e01160000000fd81ce49a9480000f8163efffea1f9ae0101fa163e940598
++ns_packet=3333ffa1f9aefa163e94059886dd6000000000203afffd81ce49a9480000f8163efffe940598ff0200000000000000000001ffa1f9ae8700e01160000000fd81ce49a9480000f8163efffea1f9ae0101fa163e940598
+ na_packet=fa163e940598fa163ea1f9ae86dd6000000000203afffd81ce49a9480000f8163efffea1f9aefd81ce49a9480000f8163efffe9405988800e9ed60000000fd81ce49a9480000f8163efffea1f9ae0201fa163ea1f9ae
+ 
+ as hv1 ovs-appctl netdev-dummy/receive vif1 $ns_packet
+@@ -14417,7 +14433,7 @@ test_ns_na() {
+     local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
+ 
+     packet=$(fmt_pkt "
+-        Ether(dst='ff:ff:ff:ff:ff:ff', src='${src_mac}') /
++        Ether(dst='33:33:ff:ff:ff:ff', src='${src_mac}') /
+         IPv6(src='${src_ip}', dst='ff02::1:ff00:2') /
+         ICMPv6ND_NS(tgt='${dst_ip}')
+     ")
+@@ -15740,14 +15756,17 @@ m4_define([MULTICHASSIS_PATH_MTU_DISCOVERY_TEST],
+    second_mac=00:00:00:00:00:02
+    multi1_mac=00:00:00:00:00:f0
+    multi2_mac=00:00:00:00:00:f1
++   external_mac=00:00:00:00:ee:ff
+    first_ip=10.0.0.1
+    second_ip=10.0.0.2
+    multi1_ip=10.0.0.10
+    multi2_ip=10.0.0.20
++   external_ip=10.0.0.30
+    first_ip6=abcd::1
+    second_ip6=abcd::2
+    multi1_ip6=abcd::f0
+    multi2_ip6=abcd::f1
++   external_ip6=abcd::eeff
+ 
+    check ovn-nbctl ls-add ls0
+    check ovn-nbctl lsp-add ls0 first
+@@ -15866,6 +15885,24 @@ m4_define([MULTICHASSIS_PATH_MTU_DISCOVERY_TEST],
+ 
+    reset_env
+ 
++   AS_BOX([Multi with "unknown" to external doesn't produce wrong FDB])
++   len=3000
++   check ovn-nbctl --wait=hv lsp-set-addresses multi1 unknown
++
++   packet=$(send_ip_packet multi1 1 $multi1_mac $external_mac $multi1_ip $external_ip $(payload $len) 1 ${expected_ip_mtu})
++   echo $packet >> hv1/multi1.expected
++
++   packet=$(send_ip6_packet multi1 1 $multi1_mac $external_mac $multi1_ip6 $external_ip6 $(payload $len) 1 ${expected_ip_mtu})
++   echo $packet >> hv1/multi1.expected
++
++   check_pkts
++   reset_env
++
++   check_row_count fdb 0 mac="$external_mac"
++   ovn-sbctl --all destroy fdb
++
++   check ovn-nbctl --wait=hv lsp-set-addresses multi1 "${multi1_mac} ${multi1_ip} ${multi1_ip6}"
++
+    AS_BOX([Packets of proper size are delivered from multichassis to regular ports])
+ 
+    len=1000
+@@ -15965,9 +16002,6 @@ m4_define([MULTICHASSIS_PATH_MTU_DISCOVERY_TEST],
+    packet=$(send_ip6_packet multi1 1 $multi1_mac $multi2_mac $multi1_ip6 $multi2_ip6 $(payload $len) 1)
+    echo $packet >> hv1/multi1.expected
+ 
+-   check_pkts
+-   reset_env
+-
+    AS_BOX([MTU updates are honored in ICMP Path MTU calculation])
+ 
+    set_mtu() {
+@@ -28717,7 +28751,7 @@ AT_CHECK([
+     for hv in 1 2; do
+         grep table=$ecmp_stateful hv${hv}flows | \
+         grep "priority=100" | \
+-        grep -c "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],.*exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_MARK\\[[16..31\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[96..127\\]]))"
++        grep -c "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],.*exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_MARK\\[[16..31\\]]))"
+ 
+         grep table=$arp_resolve hv${hv}flows | \
+         grep "priority=200" | \
+@@ -28846,7 +28880,7 @@ AT_CHECK([
+     for hv in 1 2; do
+         grep table=$ecmp_stateful hv${hv}flows | \
+         grep "priority=100" | \
+-        grep -c "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],.*exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_MARK\\[[16..31\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[96..127\\]]))"
++        grep -c "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],.*exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_MARK\\[[16..31\\]]))"
+ 
+         grep table=$arp_resolve hv${hv}flows | \
+         grep "priority=200" | \
+@@ -34709,11 +34743,13 @@ port_key_1=$(printf "0x%x" $(as hv1 fetch_column port_binding tunnel_key logical
+ dp_key_2=$(printf "0x%x" $(as hv1 fetch_column datapath tunnel_key external_ids:name=gw-2))
+ port_key_2=$(printf "0x%x" $(as hv1 fetch_column port_binding tunnel_key logical_port=gw-2-public))
+ 
+-AT_CHECK_UNQUOTED([as hv1 ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE --no-stats | strip_cookie | sort], [0], [dnl
+- table=OFTABLE_MAC_CACHE_USE, priority=100,ip,reg14=${port_key_1},metadata=${dp_key_1},dl_src=00:00:00:00:10:10,nw_src=192.168.10.10 actions=drop
++table=" table=OFTABLE_MAC_CACHE_USE, priority=100,ip,reg14=${port_key_1},metadata=${dp_key_1},dl_src=00:00:00:00:10:10,nw_src=192.168.10.10 actions=drop
+  table=OFTABLE_MAC_CACHE_USE, priority=100,ip,reg14=${port_key_1},metadata=${dp_key_1},dl_src=00:00:00:00:10:20,nw_src=192.168.10.20 actions=drop
+  table=OFTABLE_MAC_CACHE_USE, priority=100,ip,reg14=${port_key_2},metadata=${dp_key_2},dl_src=00:00:00:00:10:10,nw_src=192.168.10.10 actions=drop
+- table=OFTABLE_MAC_CACHE_USE, priority=100,ip,reg14=${port_key_2},metadata=${dp_key_2},dl_src=00:00:00:00:10:20,nw_src=192.168.10.20 actions=drop
++ table=OFTABLE_MAC_CACHE_USE, priority=100,ip,reg14=${port_key_2},metadata=${dp_key_2},dl_src=00:00:00:00:10:20,nw_src=192.168.10.20 actions=drop"
++sorted_table=$(printf '%s\n' "$table" | sort)
++AT_CHECK_UNQUOTED([as hv1 ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE --no-stats | strip_cookie | sort], [0], [dnl
++$sorted_table
+ ])
+ 
+ timestamp=$(fetch_column mac_binding timestamp ip="192.168.10.20")
+@@ -38238,49 +38274,60 @@ AT_CLEANUP
+ OVN_FOR_EACH_NORTHD([
+ AT_SETUP([ovn-controller - cleanup VIF/CIF related flows/fields when updating requested-chassis])
+ ovn_start
+-
+ net_add n1
+-sim_add hv1
+-ovs-vsctl add-br br-phys
+-ovn_attach n1 br-phys 192.168.0.1
+-check ovs-vsctl -- add-port br-int vif1 -- \
+-    set Interface vif1 external-ids:iface-id=lsp1 \
+-    ofport-request=8
+ 
+-check ovn-nbctl ls-add lsw0
++for i in 1 2; do
++    sim_add hv$i
++    as hv$i
++    ovs-vsctl add-br br-phys
++    ovn_attach n1 br-phys 192.168.0.$i
++    check ovs-vsctl -- add-port br-int vif1 -- \
++        set Interface vif1 ofport-request=8
++done
+ 
++check ovn-nbctl ls-add lsw0
++as hv1
++check ovs-vsctl set Interface vif1 external-ids:iface-id=lsp1
+ check ovn-nbctl lsp-add lsw0 lsp1
+ check ovn-nbctl lsp-add lsw0 sw0-port1.1 lsp1 7
+ 
+ # wait for the VIF to be claimed to this chassis
+ wait_row_count Chassis 1 name=hv1
++wait_row_count Chassis 1 name=hv2
+ hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
++hv2_uuid=$(fetch_column Chassis _uuid name=hv2)
++
+ wait_for_ports_up lsp1
+ wait_for_ports_up sw0-port1.1
+ wait_column "$hv1_uuid" Port_Binding chassis logical_port=lsp1
+ wait_column "$hv1_uuid" Port_Binding chassis logical_port=sw0-port1.1
+ 
+ # check that flows is installed
+-OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-flows br-int table=0 |grep priority=100 | grep -c in_port=8], [0],[dnl
++OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-flows br-int table=OFTABLE_PHY_TO_LOG |grep priority=150|grep dl_vlan=7| grep -c in_port=8], [0],[dnl
+ 1
+ ])
+-OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-flows br-int table=0 |grep priority=150|grep dl_vlan=7| grep -c in_port=8], [0],[dnl
+-1
++
++OVS_WAIT_FOR_OUTPUT([as hv2 ovs-ofctl dump-flows br-int table=OFTABLE_PHY_TO_LOG |grep priority=150|grep dl_vlan=7| grep -c in_port=8], [1],[dnl
++0
+ ])
+ 
+-# set lport requested-chassis to differant chassis
++# Add hv2 to lport Additional requested chassis as MAIN chassis
++# and check that no flows installed in table 0 in hv1
+ check ovn-nbctl set Logical_Switch_Port lsp1 \
+-    options:requested-chassis=foo
++    options:requested-chassis=hv2,hv1
+ 
+-OVS_WAIT_UNTIL([test `ovn-sbctl get Port_Binding lsp1 up` = 'false'])
+-OVS_WAIT_UNTIL([test `ovn-sbctl get Port_Binding sw0-port1.1 up` = 'false'])
+-wait_column "" Port_Binding chassis logical_port=lsp1
+-wait_column "" Port_Binding chassis logical_port=sw0-port1.1
++as hv2
++check ovs-vsctl set Interface vif1 external-ids:iface-id=lsp1
++ovn-nbctl --wait=hv sync
+ 
+-OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-flows br-int table=0 |grep priority=100 |grep -c in_port=8], [1],[dnl
+-0
++wait_for_ports_up lsp1
++wait_for_ports_up sw0-port1.1
++wait_column "$hv2_uuid" Port_Binding chassis logical_port=lsp1
++
++OVS_WAIT_FOR_OUTPUT([as hv2 ovs-ofctl dump-flows br-int table=OFTABLE_PHY_TO_LOG |grep priority=150|grep dl_vlan=7| grep -c in_port=8], [0],[dnl
++1
+ ])
+-OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-flows br-int table=0 |grep priority=150|grep dl_vlan=7| grep -c in_port=8], [1],[dnl
++OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-flows br-int table=OFTABLE_PHY_TO_LOG |grep priority=150|grep dl_vlan=7| grep -c in_port=8], [1],[dnl
+ 0
+ ])
+ 
+@@ -38756,3 +38803,238 @@ OVN_CLEANUP([hv1],[hv2])
+ 
+ AT_CLEANUP
+ ])
++
++dnl This test checks that the megaflows translated by ovs-vswitchd
++dnl don't match on IPv6 source and destination addresses for
++dnl simple switching.
++OVN_FOR_EACH_NORTHD([
++AT_SETUP([IPv6 switching - megaflow check for IPv6 src/dst matches])
++AT_SKIP_IF([test $HAVE_SCAPY = no])
++ovn_start
++
++check ovn-nbctl ls-add sw0
++
++check ovn-nbctl lsp-add sw0 vm0
++check ovn-nbctl lsp-set-addresses vm0 "f0:00:0f:01:02:03 10.0.0.3 1000::3"
++
++check ovn-nbctl lsp-add sw0 vm1
++check ovn-nbctl lsp-set-addresses vm1 "f0:00:0f:01:02:04 10.0.0.4 1000::4"
++
++check ovn-nbctl lr-add lr0
++check ovn-nbctl lrp-add lr0 lr0-sw0 fa:16:3e:00:00:01 10.0.0.1 1000::1/64
++check ovn-nbctl lsp-add sw0 sw0-lr0
++check ovn-nbctl lsp-set-type sw0-lr0 router
++check ovn-nbctl lsp-set-addresses sw0-lr0 router
++check ovn-nbctl --wait=hv lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++net_add n1
++sim_add hv
++as hv
++check ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.1
++check ovs-vsctl add-port br-int vif1 -- \
++    set Interface vif1 external-ids:iface-id=vm0 \
++    options:tx_pcap=hv/vif1-tx.pcap \
++    options:rxq_pcap=hv/vif1-rx.pcap \
++    ofport-request=1
++check ovs-vsctl add-port br-int vif2 -- \
++    set Interface vif2  external-ids:iface-id=vm1 \
++    options:tx_pcap=hv/vif2-tx.pcap \
++    options:rxq_pcap=hv/vif2-rx.pcap \
++    ofport-request=2
++
++check ovn-nbctl --wait=sb sync
++wait_for_ports_up
++
++AS_BOX([No port security, to vm1])
++packet=$(fmt_pkt "Ether(dst='f0:00:0f:01:02:04', src='f0:00:0f:01:02:03')/ \
++                  IPv6(dst='1000::4', src='1000::3')/ \
++                  UDP(sport=53, dport=4369)")
++
++as hv
++ovs-appctl ofproto/trace br-int in_port=1 $packet > vm0_ip6_ofproto_trace.txt
++ovs-appctl netdev-dummy/receive vif1 $packet
++
++AT_CAPTURE_FILE([vm0_ip6_ofproto_trace.txt])
++
++AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [1], [dnl
++0
++])
++
++dnl Make sure that the packet was received by vm1.  This ensures that the
++dnl packet was delivered as expected and the megaflow didn't have any matches
++dnl on IPv6 src or dst.
++
++echo $packet >> expected-vif2
++OVN_CHECK_PACKETS([hv/vif2-tx.pcap], [expected-vif2])
++
++AS_BOX([No port security, to vm0])
++packet=$(fmt_pkt "Ether(dst='f0:00:0f:01:02:03', src='f0:00:0f:01:02:04')/ \
++                  IPv6(dst='1000::3', src='1000::4')/ \
++                  UDP(sport=53, dport=4369)")
++
++as hv
++ovs-appctl ofproto/trace br-int in_port=2 $packet > vm1_ip6_ofproto_trace.txt
++ovs-appctl netdev-dummy/receive vif2 $packet
++
++AT_CAPTURE_FILE([vm1_ip6_ofproto_trace.txt])
++
++AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [1], [dnl
++0
++])
++
++dnl Make sure that the packet was received by vm0.  This ensures that the
++dnl packet was delivered as expected and the megaflow didn't have any matches
++dnl on IPv6 src or dst.
++echo $packet >> expected-vif1
++OVN_CHECK_PACKETS([hv/vif1-tx.pcap], [expected-vif1])
++
++AS_BOX([With port security, to vm1])
++dnl Add port security to vm0.  The megaflow should now match on ipv6 src/dst.
++check ovn-nbctl lsp-set-port-security vm0 "f0:00:0f:01:02:03 10.0.0.3 1000::3"
++check ovn-nbctl --wait=hv sync
++
++packet=$(fmt_pkt "Ether(dst='f0:00:0f:01:02:04', src='f0:00:0f:01:02:03')/ \
++                  IPv6(dst='1000::4', src='1000::3')/ \
++                  UDP(sport=53, dport=4369)")
++
++as hv
++ovs-appctl ofproto/trace br-int in_port=1 $packet > vm0_ip6_ofproto_trace.txt
++ovs-appctl netdev-dummy/receive vif1 $packet
++
++AT_CAPTURE_FILE([vm0_ip6_ofproto_trace.txt])
++
++AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [0], [dnl
++1
++])
++
++dnl Make sure that the packet was received by vm1.
++echo $packet >> expected-vif2
++OVN_CHECK_PACKETS([hv/vif2-tx.pcap], [expected-vif2])
++
++AS_BOX([Clear port security, to vm1])
++dnl Clear port security.
++check ovn-nbctl lsp-set-port-security vm0 ""
++check ovn-nbctl --wait=hv sync
++
++as hv
++ovs-appctl ofproto/trace br-int in_port=1 $packet > vm0_ip6_ofproto_trace.txt
++ovs-appctl netdev-dummy/receive vif1 $packet
++
++AT_CAPTURE_FILE([vm0_ip6_ofproto_trace.txt])
++
++AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [1], [dnl
++0
++])
++
++dnl Make sure that the packet was received by vm1.
++echo $packet >> expected-vif2
++OVN_CHECK_PACKETS([hv/vif2-tx.pcap], [expected-vif2])
++
++AS_BOX([With proxy arp/nd, to vm1])
++dnl Configure proxy arp/nd on the router port.  The megaflow should now match
++dnl on ipv6 src/dst.
++check ovn-nbctl --wait=hv lsp-set-options sw0-lr0 router-port=lr0-sw0 arp_proxy="2000::1/64"
++
++as hv
++ovs-appctl ofproto/trace br-int in_port=1 $packet > vm0_ip6_ofproto_trace.txt
++ovs-appctl netdev-dummy/receive vif1 $packet
++
++AT_CAPTURE_FILE([vm0_ip6_ofproto_trace.txt])
++
++AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [0], [dnl
++1
++])
++
++dnl Make sure that the packet was received by vm1.
++echo $packet >> expected-vif2
++OVN_CHECK_PACKETS([hv/vif2-tx.pcap], [expected-vif2])
++
++AS_BOX([With proxy arp/nd, to vm0])
++packet=$(fmt_pkt "Ether(dst='f0:00:0f:01:02:03', src='f0:00:0f:01:02:04')/ \
++                  IPv6(dst='1000::3', src='1000::4')/ \
++                  UDP(sport=53, dport=4369)")
++
++as hv
++ovs-appctl ofproto/trace br-int in_port=2 $packet > vm1_ip6_ofproto_trace.txt
++ovs-appctl netdev-dummy/receive vif2 $packet
++
++AT_CAPTURE_FILE([vm1_ip6_ofproto_trace.txt])
++
++AT_CHECK([grep Megaflow vm0_ip6_ofproto_trace.txt | grep -e ipv6_src -e ipv6_dst -c], [0], [dnl
++1
++])
++
++dnl Make sure that the packet was received by vm0.
++echo $packet >> expected-vif1
++OVN_CHECK_PACKETS([hv/vif1-tx.pcap], [expected-vif1])
++
++AT_CLEANUP
++])
++
++
++OVN_FOR_EACH_NORTHD([
++AT_SETUP([Multichassis port I-P processing])
++ovn_start
++
++net_add n1
++
++sim_add hv1
++as hv1
++check ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.11
++check ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++
++sim_add hv2
++as hv2
++check ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.12
++check ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++
++check ovn-nbctl ls-add ls
++check ovn-nbctl lsp-add ls multi
++check ovn-nbctl lsp-set-options multi requested-chassis=hv1
++
++check ovn-nbctl lsp-add ls ln
++check ovn-nbctl lsp-set-type ln localnet
++check ovn-nbctl lsp-set-addresses ln unknown
++check ovn-nbctl lsp-set-options ln network_name=phys
++
++check ovn-nbctl lsp-add ls lsp1 \
++    -- lsp-set-options lsp1 requested-chassis=hv1
++as hv1 check ovs-vsctl -- add-port br-int lsp1 \
++    -- set Interface lsp1 external-ids:iface-id=lsp1
++
++for hv in hv1 hv2; do
++    as $hv check ovs-vsctl -- add-port br-int multi \
++        -- set Interface multi external-ids:iface-id=multi
++done
++
++wait_for_ports_up
++ovn-nbctl --wait=hv sync
++
++OVS_WAIT_UNTIL([test $(as hv2 ovs-ofctl dump-flows br-int table=OFTABLE_OUTPUT_LARGE_PKT_DETECT | grep -c check_pkt_larger) -eq 0])
++
++check ovn-nbctl --wait=hv lsp-set-options multi requested-chassis=hv1,hv2
++OVS_WAIT_UNTIL([test $(as hv2 ovs-ofctl dump-flows br-int table=OFTABLE_OUTPUT_LARGE_PKT_DETECT | grep -c check_pkt_larger) -eq 4])
++
++check ovn-nbctl --wait=hv lsp-set-options multi requested-chassis=hv1
++OVS_WAIT_UNTIL([test $(as hv2 ovs-ofctl dump-flows br-int table=OFTABLE_OUTPUT_LARGE_PKT_DETECT | grep -c check_pkt_larger) -eq 0])
++
++check ovn-nbctl --wait=hv lsp-set-options multi requested-chassis=hv1,hv2
++OVS_WAIT_UNTIL([test $(as hv2 ovs-ofctl dump-flows br-int table=OFTABLE_OUTPUT_LARGE_PKT_DETECT | grep -c check_pkt_larger) -eq 4])
++
++as hv2 check ovs-vsctl del-port multi
++OVS_WAIT_UNTIL([test $(as hv2 ovs-ofctl dump-flows br-int table=OFTABLE_OUTPUT_LARGE_PKT_DETECT | grep -c check_pkt_larger) -eq 0])
++
++as hv2 check ovs-vsctl -- add-port br-int multi \
++    -- set Interface multi external-ids:iface-id=multi
++OVS_WAIT_UNTIL([test $(as hv2 ovs-ofctl dump-flows br-int table=OFTABLE_OUTPUT_LARGE_PKT_DETECT | grep -c check_pkt_larger) -eq 4])
++
++check ovn-nbctl --wait=hv lsp-del multi
++OVS_WAIT_UNTIL([test $(as hv2 ovs-ofctl dump-flows br-int table=OFTABLE_OUTPUT_LARGE_PKT_DETECT | grep -c check_pkt_larger) -eq 0])
++
++OVN_CLEANUP([hv1],[hv2])
++
++AT_CLEANUP
++])
+diff --git a/tests/system-ovn.at b/tests/system-ovn.at
+index c54b0f3a5..861b1cb99 100644
+--- a/tests/system-ovn.at
++++ b/tests/system-ovn.at
+@@ -3518,7 +3518,7 @@ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:0
+ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.1.3 foo2 00:00:02:02:03:05])
+ 
+ # Add a SNAT rule
+-AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16])
++AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 0.0.0.0/0])
+ 
+ # Add default route to ext-net
+ AT_CHECK([ovn-nbctl lr-route-add R1 10.0.0.0/24 172.16.1.2])
+@@ -3724,8 +3724,7 @@ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1 00:00:02:02
+ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd11::3 foo2 00:00:02:02:03:05])
+ 
+ # Add a SNAT rule
+-AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64])
+-AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd12::/64])
++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 ::/0])
+ 
+ ovn-nbctl --wait=hv sync
+ OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd20::1)'])
+@@ -3920,7 +3919,7 @@ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:0
+ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.2.2 bar1 00:00:02:02:03:05])
+ 
+ # Add a SNAT rule
+-AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16])
++AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 0.0.0.0/0])
+ 
+ ovn-nbctl --wait=hv sync
+ OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)'])
+@@ -4104,8 +4103,7 @@ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1 00:00:02:02
+ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd12::2 bar1 00:00:02:02:03:05])
+ 
+ # Add a SNAT rule
+-AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64])
+-AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd12::/64])
++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 ::/0])
+ 
+ ovn-nbctl --wait=hv sync
+ OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd20::1)'])
+@@ -6172,21 +6170,18 @@ NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.2 | FORMAT_PING], \
+ # and just ensure that the known ethernet address is present.
+ AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.0.1) | \
+ sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
+-sed -e 's/mark=[[0-9]]*/mark=<cleared>/' |
+-sed -e 's/labels=0x[[0-9]]/labels=0x?/'], [0], [dnl
+-icmp,orig=(src=172.16.0.1,dst=10.0.0.2,id=<cleared>,type=8,code=0),reply=(src=10.0.0.2,dst=172.16.0.1,id=<cleared>,type=0,code=0),zone=<cleared>,mark=<cleared>,labels=0x?000000000401020400000000
+-tcp,orig=(src=172.16.0.1,dst=10.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.2,dst=172.16.0.1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x?000000000401020400000000,protoinfo=(state=<cleared>)
++sed -e 's/mark=[[0-9]]*/mark=<cleared>/'], [0], [dnl
++icmp,orig=(src=172.16.0.1,dst=10.0.0.2,id=<cleared>,type=8,code=0),reply=(src=10.0.0.2,dst=172.16.0.1,id=<cleared>,type=0,code=0),zone=<cleared>,mark=<cleared>,labels=0x401020400000000
++tcp,orig=(src=172.16.0.1,dst=10.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.2,dst=172.16.0.1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x401020400000000,protoinfo=(state=<cleared>)
+ ])
+ 
+ # Ensure datapaths show conntrack states as expected
+ # Like with conntrack entries, we shouldn't try to predict
+ # port binding tunnel keys. So omit them from expected labels.
+-AT_CHECK([ovs-appctl dpctl/dump-flows | sed -e 's/label=0x[[0-9]]/label=0x?/' | \
+-grep 'ct_state(+new-est+trk).*ct(.*label=0x?000000000401020400000000/.*)' -c], [0], [dnl
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(+new-est+trk).*ct(.*label=0x401020400000000/.*)' -c], [0], [dnl
+ 2
+ ])
+-AT_CHECK([[ovs-appctl dpctl/dump-flows | sed -e 's/ct_label(0x[0-9]/ct_label(0x?/' | \
+-grep 'ct_state(-new+est+trk).*ct_label(0x?000000000401020400000000)' -c]], [0], [dnl
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+trk).*ct_label(0x401020400000000)' -c], [0], [dnl
+ 2
+ ])
+ 
+@@ -6205,21 +6200,18 @@ NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.2 | FORMAT_PING], \
+ [0], [dnl
+ 3 packets transmitted, 3 received, 0% packet loss, time 0ms
+ ])
+-AT_CHECK([ovs-appctl dpctl/dump-flows | sed -e 's/label=0x[[0-9]]/label=0x?/' | \
+-grep 'ct_state(+new-est+trk).*ct(.*label=0x?000000001001020400000000/.*)' -c], [0], [dnl
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(+new-est+trk).*ct(.*label=0x1001020400000000/.*)' -c], [0], [dnl
+ 2
+ ])
+-AT_CHECK([[ovs-appctl dpctl/dump-flows | sed -e 's/ct_label(0x[0-9]/ct_label(0x?/' | \
+-grep 'ct_state(-new+est+trk).*ct_label(0x?000000001001020400000000)' -c]], [0], [dnl
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+trk).*ct_label(0x1001020400000000)' -c], [0], [dnl
+ 2
+ ])
+ 
+-AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 1001020400000000 | FORMAT_CT(172.16.0.1) | \
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 0x1001020400000000 | FORMAT_CT(172.16.0.1) | \
+ sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
+-sed -e 's/mark=[[0-9]]*/mark=<cleared>/' |
+-sed -e 's/labels=0x[[0-9]]/labels=0x?/' | sort], [0], [dnl
+-icmp,orig=(src=172.16.0.1,dst=10.0.0.2,id=<cleared>,type=8,code=0),reply=(src=10.0.0.2,dst=172.16.0.1,id=<cleared>,type=0,code=0),zone=<cleared>,mark=<cleared>,labels=0x?000000001001020400000000
+-tcp,orig=(src=172.16.0.1,dst=10.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.2,dst=172.16.0.1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x?000000001001020400000000,protoinfo=(state=<cleared>)
++sed -e 's/mark=[[0-9]]*/mark=<cleared>/' | sort], [0], [dnl
++icmp,orig=(src=172.16.0.1,dst=10.0.0.2,id=<cleared>,type=8,code=0),reply=(src=10.0.0.2,dst=172.16.0.1,id=<cleared>,type=0,code=0),zone=<cleared>,mark=<cleared>,labels=0x1001020400000000
++tcp,orig=(src=172.16.0.1,dst=10.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.2,dst=172.16.0.1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x1001020400000000,protoinfo=(state=<cleared>)
+ ])
+ # Check entries in table 76 and 77 expires w/o traffic
+ OVS_WAIT_UNTIL([
+@@ -6241,16 +6233,27 @@ NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 172.16.0.1 | FORMAT_PING], \
+ 3 packets transmitted, 3 received, 0% packet loss, time 0ms
+ ])
+ 
+-AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 401020500000000 | FORMAT_CT(172.16.0.1) | \
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 0x401020500000000 | FORMAT_CT(172.16.0.1) | \
+ sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
+-sed -e 's/mark=[[0-9]]*/mark=<cleared>/' |
+-sed -e 's/labels=0x[[0-9]]/labels=0x?/' | sort], [0], [dnl
+-tcp,orig=(src=172.16.0.1,dst=10.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.2,dst=172.16.0.1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x?000000000401020500000000,protoinfo=(state=<cleared>)
++sed -e 's/mark=[[0-9]]*/mark=<cleared>/' | sort], [0], [dnl
++tcp,orig=(src=172.16.0.1,dst=10.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.2,dst=172.16.0.1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x401020500000000,protoinfo=(state=<cleared>)
+ ])
+ 
+-# Flush connection tracking entries
+-ovn-nbctl --wait=hv lr-route-del R1
+-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.0.1)])
++# Now remove one ECMP route and check that traffic is still being conntracked.
++check ovn-nbctl --policy="src-ip" lr-route-del R1 10.0.0.0/24 20.0.0.3
++check ovn-nbctl --wait=hv sync
++AT_CHECK([ovs-appctl dpctl/flush-conntrack])
++NETNS_DAEMONIZE([bob1], [nc -l -k 8081], [bob2.pid])
++NS_CHECK_EXEC([alice1], [nc -z 172.16.0.1 8081], [0])
++NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 172.16.0.1 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 0x401020500000000 | FORMAT_CT(172.16.0.1) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
++sed -e 's/mark=[[0-9]]*/mark=<cleared>/' | sort], [0], [dnl
++tcp,orig=(src=172.16.0.1,dst=10.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.2,dst=172.16.0.1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x401020500000000,protoinfo=(state=<cleared>)
++])
+ 
+ OVS_APP_EXIT_AND_WAIT([ovn-controller])
+ 
+@@ -6399,12 +6402,11 @@ NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 fd01::2 | FORMAT_PING], \
+ # Ensure datapaths show conntrack states as expected
+ # Like with conntrack entries, we shouldn't try to predict
+ # port binding tunnel keys. So omit them from expected labels.
+-AT_CHECK([ovs-appctl dpctl/dump-flows | sed -e 's/label=0x[[0-9]]/label=0x?/' | \
+-grep 'ct_state(+new-est+trk).*ct(.*label=0x?000000000401020400000000/.*)' -c], [0], [dnl
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(+new-est+trk).*ct(.*label=0x401020400000000/.*)' -c], [0], [dnl
+ 2
+ ])
+-AT_CHECK([[ovs-appctl dpctl/dump-flows | sed -e 's/ct_label(0x[0-9]/ct_label(0x?/' | \
+-grep 'ct_state(-new+est+trk).*ct_label(0x?000000000401020400000000)' -c]], [0], [dnl
++
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+trk).*ct_label(0x401020400000000)' -c], [0], [dnl
+ 2
+ ])
+ 
+@@ -6413,10 +6415,9 @@ grep 'ct_state(-new+est+trk).*ct_label(0x?000000000401020400000000)' -c]], [0],
+ # and just ensure that the known ethernet address is present.
+ AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd01::2) | \
+ sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
+-sed -e 's/mark=[[0-9]]*/mark=<cleared>/' |
+-sed -e 's/labels=0x[[0-9]]/labels=0x?/' | sort], [0], [dnl
+-icmpv6,orig=(src=fd07::1,dst=fd01::2,id=<cleared>,type=128,code=0),reply=(src=fd01::2,dst=fd07::1,id=<cleared>,type=129,code=0),zone=<cleared>,mark=<cleared>,labels=0x?000000000401020400000000
+-tcp,orig=(src=fd07::1,dst=fd01::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd07::1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x?000000000401020400000000,protoinfo=(state=<cleared>)
++sed -e 's/mark=[[0-9]]*/mark=<cleared>/' | sort], [0], [dnl
++icmpv6,orig=(src=fd07::1,dst=fd01::2,id=<cleared>,type=128,code=0),reply=(src=fd01::2,dst=fd07::1,id=<cleared>,type=129,code=0),zone=<cleared>,mark=<cleared>,labels=0x401020400000000
++tcp,orig=(src=fd07::1,dst=fd01::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd07::1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x401020400000000,protoinfo=(state=<cleared>)
+ ])
+ 
+ # Flush conntrack entries for easier output parsing of next test.
+@@ -6433,21 +6434,18 @@ NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 fd01::2 | FORMAT_PING], \
+ 3 packets transmitted, 3 received, 0% packet loss, time 0ms
+ ])
+ 
+-AT_CHECK([ovs-appctl dpctl/dump-flows | sed -e 's/label=0x[[0-9]]/label=0x?/' | \
+-grep 'ct_state(+new-est+trk).*ct(.*label=0x?000000001001020400000000/.*)' -c], [0], [dnl
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(+new-est+trk).*ct(.*label=0x1001020400000000/.*)' -c], [0], [dnl
+ 2
+ ])
+-AT_CHECK([[ovs-appctl dpctl/dump-flows | sed -e 's/ct_label(0x[0-9]/ct_label(0x?/' | \
+-grep 'ct_state(-new+est+trk).*ct_label(0x?000000001001020400000000)' -c]], [0], [dnl
++AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+trk).*ct_label(0x1001020400000000)' -c], [0], [dnl
+ 2
+ ])
+ 
+-AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 1001020400000000 | FORMAT_CT(fd01::2) | \
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 0x1001020400000000 | FORMAT_CT(fd01::2) | \
+ sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
+-sed -e 's/mark=[[0-9]]*/mark=<cleared>/' |
+-sed -e 's/labels=0x[[0-9]]/labels=0x?/'], [0], [dnl
+-icmpv6,orig=(src=fd07::1,dst=fd01::2,id=<cleared>,type=128,code=0),reply=(src=fd01::2,dst=fd07::1,id=<cleared>,type=129,code=0),zone=<cleared>,mark=<cleared>,labels=0x?000000001001020400000000
+-tcp,orig=(src=fd07::1,dst=fd01::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd07::1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x?000000001001020400000000,protoinfo=(state=<cleared>)
++sed -e 's/mark=[[0-9]]*/mark=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd07::1,dst=fd01::2,id=<cleared>,type=128,code=0),reply=(src=fd01::2,dst=fd07::1,id=<cleared>,type=129,code=0),zone=<cleared>,mark=<cleared>,labels=0x1001020400000000
++tcp,orig=(src=fd07::1,dst=fd01::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd07::1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x1001020400000000,protoinfo=(state=<cleared>)
+ ])
+ 
+ # Check entries in table 76 and 77 expires w/o traffic
+@@ -6467,16 +6465,27 @@ NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 fd07::1 | FORMAT_PING], \
+ 3 packets transmitted, 3 received, 0% packet loss, time 0ms
+ ])
+ 
+-AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 1001020400000000 | FORMAT_CT(fd07::1) | \
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 0x1001020400000000 | FORMAT_CT(fd07::1) | \
+ sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
+-sed -e 's/mark=[[0-9]]*/mark=<cleared>/' |
+-sed -e 's/labels=0x[[0-9]]/labels=0x?/' | sort], [0], [dnl
+-tcp,orig=(src=fd07::1,dst=fd01::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd07::1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x?000000001001020400000000,protoinfo=(state=<cleared>)
++sed -e 's/mark=[[0-9]]*/mark=<cleared>/' | sort], [0], [dnl
++tcp,orig=(src=fd07::1,dst=fd01::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd07::1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x1001020400000000,protoinfo=(state=<cleared>)
+ ])
+ 
+-# Flush connection tracking entries
+-check ovn-nbctl --wait=hv lr-route-del R1
+-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd01::2)])
++# Now remove one ECMP route and check that traffic is still being conntracked.
++check ovn-nbctl --policy="src-ip" lr-route-del R1 fd01::/126 fd02::3
++check ovn-nbctl --wait=hv sync
++AT_CHECK([ovs-appctl dpctl/flush-conntrack])
++NETNS_DAEMONIZE([bob1], [nc -6 -l -k 8081], [bob2.pid])
++NS_CHECK_EXEC([alice1], [nc -6 -z fd07::1 8081], [0])
++NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 fd07::1 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep 0x1001020400000000 | FORMAT_CT(fd07::1) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/' |
++sed -e 's/mark=[[0-9]]*/mark=<cleared>/' | sort], [0], [dnl
++tcp,orig=(src=fd07::1,dst=fd01::2,sport=<cleared>,dport=<cleared>),reply=(src=fd01::2,dst=fd07::1,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=<cleared>,labels=0x1001020400000000,protoinfo=(state=<cleared>)
++])
+ 
+ OVS_APP_EXIT_AND_WAIT([ovn-controller])
+ 
+@@ -6941,7 +6950,8 @@ check ovn-nbctl lsp-add public public1 \
+         -- lsp-set-type public1 localnet \
+         -- lsp-set-options public1 network_name=phynet
+ 
+-NS_CHECK_EXEC([server], [bfdd-beacon --listen=172.16.1.50], [0])
++NETNS_DAEMONIZE([server], [bfdd-beacon  --nofork --tee --listen=172.16.1.50 >beacon.stdout 2>&1], [beacon.pid])
++OVS_WAIT_UNTIL([grep -q "Listening for BFD connections" beacon.stdout])
+ NS_CHECK_EXEC([server], [bfdd-control allow 172.16.1.1], [0], [dnl
+ Allowing connections from 172.16.1.1
+ ])
+@@ -7001,7 +7011,8 @@ check ovn-nbctl set logical_router R1 options:chassis=hv1
+ check ovn-nbctl set logical_router_static_route $route_uuid bfd=$uuid
+ 
+ # restart bfdd
+-NS_CHECK_EXEC([server], [bfdd-beacon --listen=172.16.1.50], [0])
++NETNS_DAEMONIZE([server], [bfdd-beacon  --nofork --tee --listen=172.16.1.50 >beacon.stdout 2>&1], [beacon.pid])
++OVS_WAIT_UNTIL([grep -q "Listening for BFD connections" beacon.stdout])
+ NS_CHECK_EXEC([server], [bfdd-control allow 172.16.1.1], [0], [dnl
+ Allowing connections from 172.16.1.1
+ ])
+@@ -7043,7 +7054,8 @@ check ovn-nbctl lr-route-add R1 2000::/64 1000::b
+ route_uuid_v6=$(fetch_column nb:logical_router_static_route _uuid ip_prefix=\"2000::/64\")
+ ovn-nbctl set logical_router_static_route $route_uuid_v6 bfd=$uuid_v6
+ check ovn-nbctl --wait=hv sync
+-NS_CHECK_EXEC([server], [bfdd-beacon --listen=1000::b], [0])
++NETNS_DAEMONIZE([server], [bfdd-beacon  --nofork --tee --listen=1000::b >beacon.stdout 2>&1], [beacon.pid])
++OVS_WAIT_UNTIL([grep -q "Listening for BFD connections" beacon.stdout])
+ NS_CHECK_EXEC([server], [bfdd-control allow 1000::a], [0], [dnl
+ Allowing connections from 1000::a
+ ])
+@@ -9374,7 +9386,7 @@ name: 'vport4' value: '999'
+ NETNS_DAEMONIZE([vm1], [nc -k -l 42.42.42.2 4242], [nc-vm1.pid])
+ 
+ NETNS_START_TCPDUMP([vm1],
+-    [-n -i vm1 -nnleX -c6 udp and dst 42.42.42.2 and dst port 4343],
++    [-n -i vm1 -nnqleX -c6 udp and dst 42.42.42.2 and dst port 4343],
+     [vm1])
+ 
+ # Make sure connecting to the VIP works (hairpin, via ls and via lr).
+@@ -9525,7 +9537,7 @@ name: 'vport4' value: '999'
+ NETNS_DAEMONIZE([vm1], [nc -k -l 4242::2 4242], [nc-vm1.pid])
+ 
+ NETNS_START_TCPDUMP([vm1],
+-    [-n -i vm1 -nnleX -c6 udp and dst 4242::2 and dst port 4343],
++    [-n -i vm1 -nnqleX -c6 udp and dst 4242::2 and dst port 4343],
+     [vm1])
+ 
+ # Make sure connecting to the VIP works (hairpin, via ls and via lr).
+@@ -11363,7 +11375,25 @@ check_ovn_installed
+ check_ports_up
+ check_ports_bound
+ 
+-OVS_APP_EXIT_AND_WAIT([ovn-controller])
++AS_BOX(["Leave some ovn-installed while closing ovn-controller"])
++# Block IDL from ovn-controller to OVSDB
++stop_ovsdb_controller_updates $TCP_PORT
++remove_iface_id vif2
++ensure_controller_run
++
++# OVSDB should now be seen as read-only by ovn-controller
++remove_iface_id vif1
++check ovn-nbctl --wait=hv sync
++
++# Stop ovsdb before ovn-controller to ensure it's not updated
++as
++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
++/connection dropped.*/d"])
++
++# Don't use OVS_APP_EXIT... to use --restart to avoid cleaning up the databases.
++TMPPID=$(cat $OVS_RUNDIR/ovn-controller.pid)
++check ovs-appctl -t ovn-controller exit --restart
++OVS_WAIT_WHILE([kill -0 $TMPPID 2>/dev/null])
+ 
+ as ovn-sb
+ OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+@@ -11374,9 +11404,6 @@ OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+ as northd
+ OVS_APP_EXIT_AND_WAIT([ovn-northd])
+ 
+-as
+-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+-/connection dropped.*/d"])
+ AT_CLEANUP
+ ])
+ 
+@@ -13186,16 +13213,17 @@ ovs-vsctl --id=@br get Bridge br-int \
+     -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4242\" template_interval=1 \
+     -- --id=@cs create Flow_Sample_Collector_Set id=100 bridge=@br ipfix=@ipfix
+ 
++ovn-nbctl --wait=hv sync
+ dnl And wait for it to be up and running.
+ OVS_WAIT_UNTIL([ovs-ofctl dump-ipfix-flow br-int | grep -q '1 ids'])
+ 
+ dnl Start UDP echo server on vm2.
+-NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l 1000], [nc-vm2-1000.pid])
+-NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l 1010], [nc-vm2-1010.pid])
+-NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l 2000], [nc-vm2-2000.pid])
+-NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l 2010], [nc-vm2-2010.pid])
+-NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l 3000], [nc-vm2-3000.pid])
+-NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l 3010], [nc-vm2-3010.pid])
++NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l -m 1 1000], [nc-vm2-1000.pid])
++NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l -m 1 1010], [nc-vm2-1010.pid])
++NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l -m 1 2000], [nc-vm2-2000.pid])
++NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l -m 1 2010], [nc-vm2-2010.pid])
++NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l -m 1 3000], [nc-vm2-3000.pid])
++NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l -m 1 3010], [nc-vm2-3010.pid])
+ 
+ dnl Send traffic (2 packets) to the UDP LB1 (hits the from-lport ACL).
+ NS_CHECK_EXEC([vm1], [(echo a; sleep 1; echo a) | nc --send-only -u 43.43.43.43 1000])
+@@ -13354,11 +13382,12 @@ ovs-vsctl --id=@br get Bridge br-int \
+     -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4242\" template_interval=1 \
+     -- --id=@cs create Flow_Sample_Collector_Set id=100 bridge=@br ipfix=@ipfix
+ 
++ovn-nbctl --wait=hv sync
+ dnl And wait for it to be up and running.
+ OVS_WAIT_UNTIL([ovs-ofctl dump-ipfix-flow br-int | grep -q '1 ids'])
+ 
+ dnl Start UDP echo server on vm2.
+-NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l 1000], [nc-vm2-1000.pid])
++NETNS_DAEMONIZE([vm2], [nc -e /bin/cat -k -u -v -l -m 1 1000], [nc-vm2-1000.pid])
+ 
+ dnl Send traffic to the UDP server (hits both ACL tiers).
+ NS_CHECK_EXEC([vm1], [echo a | nc --send-only -u 42.42.42.3 1000])
+@@ -13483,6 +13512,7 @@ ovs-vsctl --id=@br get Bridge br-int \
+     -- --id=@ipfix create IPFIX targets=\"127.0.0.1:4242\" template_interval=1 \
+     -- --id=@cs create Flow_Sample_Collector_Set id=100 bridge=@br ipfix=@ipfix
+ 
++ovn-nbctl --wait=hv sync
+ dnl And wait for it to be up and running.
+ OVS_WAIT_UNTIL([ovs-ofctl dump-ipfix-flow br-int | grep -q '1 ids'])
+ 
+@@ -13750,3 +13780,152 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
+ /.*terminating with signal 15.*/d"])
+ AT_CLEANUP
+ ])
++
++OVN_FOR_EACH_NORTHD([
++AT_SETUP([Routing protocol redirect])
++AT_SKIP_IF([test $HAVE_NC = no])
++
++ovn_start
++OVS_TRAFFIC_VSWITCHD_START()
++
++ADD_BR([br-int])
++ADD_BR([br-ext])
++
++check ovs-ofctl add-flow br-ext action=normal
++# Set external-ids in br-int needed for ovn-controller
++check ovs-vsctl \
++        -- set Open_vSwitch . external-ids:system-id=hv1 \
++        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
++        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
++        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
++        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
++
++# Start ovn-controller
++start_daemon ovn-controller
++
++check ovn-nbctl lr-add R1 \
++    -- set Logical_Router R1 options:chassis=hv1
++
++check ovn-nbctl ls-add public
++check ovn-nbctl ls-add bar
++
++check ovn-nbctl lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.1/24
++check ovn-nbctl lrp-add R1 rp-bar 00:00:ff:00:00:01 192.168.10.1/24
++
++check ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
++    type=router options:router-port=rp-public \
++    -- lsp-set-addresses public-rp router
++
++check ovn-nbctl lsp-add bar bar-rp -- set Logical_Switch_Port bar-rp \
++    type=router options:router-port=rp-bar \
++    -- lsp-set-addresses bar-rp router
++
++check ovn-nbctl lsp-add public bgp-daemon \
++    -- lsp-set-addresses bgp-daemon unknown
++
++# Setup container "bar1" representing host on an internal network
++ADD_NAMESPACES(bar1)
++ADD_VETH(bar1, bar1, br-int, "192.168.10.2/24", "00:00:ff:ff:ff:01", \
++         "192.168.10.1")
++check ovn-nbctl lsp-add bar bar1 \
++    -- lsp-set-addresses bar1 "00:00:ff:ff:ff:01 192.168.10.2"
++
++# Setup SNAT for the internal host
++check ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.10.2
++
++# Configure external connectivity
++check ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext
++check ovn-nbctl lsp-add public public1 \
++        -- lsp-set-addresses public1 unknown \
++        -- lsp-set-type public1 localnet \
++        -- lsp-set-options public1 network_name=phynet
++
++check ovn-nbctl --wait=hv sync
++
++# Set option that redirects BGP and BFD traffic to a LSP "bgp-daemon"
++check ovn-nbctl --wait=sb set logical_router_port rp-public options:routing-protocol-redirect=bgp-daemon
++check ovn-nbctl --wait=sb set logical_router_port rp-public options:routing-protocols=BGP,BFD
++
++# Create "bgp-daemon" interface in a namespace with IP and MAC matching LRP "rp-public"
++ADD_NAMESPACES(bgp-daemon)
++ADD_VETH(bgp-daemon, bgp-daemon, br-int, "172.16.1.1/24", "00:00:02:01:02:03")
++
++ADD_NAMESPACES(ext-foo)
++ADD_VETH(ext-foo, ext-foo, br-ext, "172.16.1.100/24", "00:10:10:01:02:13", \
++         "172.16.1.1")
++
++# Flip the interface down/up to get proper IPv6 LLA
++NS_EXEC([bgp-daemon], [ip link set down bgp-daemon])
++NS_EXEC([bgp-daemon], [ip link set up bgp-daemon])
++NS_EXEC([ext-foo], [ip link set down ext-foo])
++NS_EXEC([ext-foo], [ip link set up ext-foo])
++
++# Wait until IPv6 LLA loses the "tentative" flag otherwise it can't be bound to.
++OVS_WAIT_UNTIL([NS_EXEC([bgp-daemon], [ip a show dev bgp-daemon | grep "fe80::" | grep -v tentative])])
++OVS_WAIT_UNTIL([NS_EXEC([ext-foo], [ip a show dev ext-foo | grep "fe80::" | grep -v tentative])])
++
++# Verify that BGP control plane traffic is delivered to the "bgp-daemon"
++# interface on both IPv4 and IPv6 LLA addresses
++NETNS_DAEMONIZE([bgp-daemon], [nc -l -k 172.16.1.1 179], [bgp_v4.pid])
++NS_CHECK_EXEC([ext-foo], [echo "BGP IPv4 server traffic" | nc --send-only 172.16.1.1 179])
++
++NETNS_DAEMONIZE([bgp-daemon], [nc -l -6 -k fe80::200:2ff:fe01:203%bgp-daemon 179], [bgp_v6.pid])
++NS_CHECK_EXEC([ext-foo], [echo "BGP IPv6 server traffic" | nc --send-only -6 fe80::200:2ff:fe01:203%ext-foo 179])
++
++# Perform same set of checks as above for BFD daemon.
++# We need to manually check that the message arrived on the receiving end as Ncat will
++# produce false positive results over UDP due to lack of ICMP port unreachable messages
++# from LRP's IP.
++NETNS_DAEMONIZE([bgp-daemon], [nc -l -u 172.16.1.1 3784 > bgp-daemon_bfd_v4.out], [bfd_v4.pid])
++NS_CHECK_EXEC([ext-foo], [echo "from ext-foo: BFD IPv4 server traffic" | nc -u 172.16.1.1 3784])
++AT_CHECK([cat bgp-daemon_bfd_v4.out], [0], [dnl
++from ext-foo: BFD IPv4 server traffic
++])
++
++NETNS_DAEMONIZE([bgp-daemon], [nc -l -6 -u fe80::200:2ff:fe01:203%bgp-daemon 3784 > bgp-daemon_bfd_v6.out], [bfd_v6.pid])
++NS_CHECK_EXEC([ext-foo], [echo "from ext-foo: BFD IPv6 server traffic" | nc -u -6 fe80::200:2ff:fe01:203%ext-foo 3784])
++AT_CHECK([cat bgp-daemon_bfd_v6.out], [0], [dnl
++from ext-foo: BFD IPv6 server traffic
++])
++
++# Verify connection in other direction. i.e when BGP daemon running on "bgp-daemon" port
++# makes a client connection to its peer
++NETNS_DAEMONIZE([ext-foo], [nc -l -k 172.16.1.100 179], [reply_bgp_v4.pid])
++NS_CHECK_EXEC([bgp-daemon], [echo "BGP IPv4 client traffic" | nc --send-only 172.16.1.100 179])
++
++NETNS_DAEMONIZE([ext-foo], [nc -l -6 -k fe80::210:10ff:fe01:213%ext-foo 179], [reply_bgp_v6.pid])
++NS_CHECK_EXEC([bgp-daemon], [echo "BGP IPv6 client traffic" | nc --send-only -6 fe80::210:10ff:fe01:213%bgp-daemon 179])
++
++# Perform same checks in other direction for BFD daemon
++NETNS_DAEMONIZE([ext-foo], [nc -l -u 172.16.1.100 3784 > ext-foo_bfd_v4.out], [reply_bfd_v4.pid])
++NS_CHECK_EXEC([bgp-daemon], [echo "from bgp-daemon: BFD IPv4 client traffic" | nc -u 172.16.1.100 3784])
++AT_CHECK([cat ext-foo_bfd_v4.out], [0], [dnl
++from bgp-daemon: BFD IPv4 client traffic
++])
++
++NETNS_DAEMONIZE([ext-foo], [nc -l -6 -u fe80::210:10ff:fe01:213%ext-foo 3784 > ext-foo_bfd_v6.out], [reply_bfd_v6.pid])
++NS_CHECK_EXEC([bgp-daemon], [echo "from bgp-daemon: BFD IPv6 client traffic" | nc -u -6 fe80::210:10ff:fe01:213%bgp-daemon 3784])
++AT_CHECK([cat ext-foo_bfd_v6.out], [0], [dnl
++from bgp-daemon: BFD IPv6 client traffic
++])
++
++# Verify that hosts on the internal network can reach external networks
++NETNS_DAEMONIZE([ext-foo], [nc -l -k 172.16.1.100 2222], [nc_external.pid])
++NS_CHECK_EXEC([bar1], [echo "TCP test" | nc -w 1 --send-only 172.16.1.100 2222])
++
++OVS_APP_EXIT_AND_WAIT([ovn-controller])
++
++as ovn-sb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as ovn-nb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as northd
++OVS_APP_EXIT_AND_WAIT([ovn-northd])
++
++as
++OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
++/.*terminating with signal 15.*/d"])
++AT_CLEANUP
++])
+diff --git a/utilities/containers/py-requirements.txt b/utilities/containers/py-requirements.txt
+index 8a3e977aa..1b55042c8 100644
+--- a/utilities/containers/py-requirements.txt
++++ b/utilities/containers/py-requirements.txt
+@@ -1,6 +1,6 @@
+ flake8>=6.1.0
+ meson>=1.4,<1.5
+-scapy
++scapy==2.5.0
+ sphinx<8.0 # https://github.com/sphinx-doc/sphinx/issues/12711
+ setuptools
+ pyelftools
diff --git a/SOURCES/ppc_64-power8-linuxapp-gcc-config b/SOURCES/ppc_64-power8-linuxapp-gcc-config
new file mode 100644
index 0000000..2319b68
--- /dev/null
+++ b/SOURCES/ppc_64-power8-linuxapp-gcc-config
@@ -0,0 +1,550 @@
+# -*- cfg-sha: ac783e64ca20c977a7c1c42e72e6dce151b31aa9aecfbfa121b45e49e938f418
+# BSD LICENSE
+# Copyright (C) IBM Corporation 2014.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of IBM Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2016 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2017 Intel Corporation
+# RTE_EXEC_ENV values are the directories in mk/exec-env/
+CONFIG_RTE_EXEC_ENV="linuxapp"
+# RTE_ARCH values are architecture we compile for. directories in mk/arch/
+CONFIG_RTE_ARCH="ppc_64"
+# machine can define specific variables or action for a specific board
+# RTE_MACHINE values are architecture we compile for. directories in mk/machine/
+CONFIG_RTE_MACHINE="power8"
+# The compiler we use.
+# RTE_TOOLCHAIN values are architecture we compile for. directories in mk/toolchain/
+CONFIG_RTE_TOOLCHAIN="gcc"
+# Use intrinsics or assembly code for key routines
+CONFIG_RTE_FORCE_INTRINSICS=n
+# Machine forces strict alignment constraints.
+CONFIG_RTE_ARCH_STRICT_ALIGN=n
+# Compile to share library
+CONFIG_RTE_BUILD_SHARED_LIB=n
+# Use newest code breaking previous ABI
+CONFIG_RTE_NEXT_ABI=n
+# Major ABI to overwrite library specific LIBABIVER
+CONFIG_RTE_MAJOR_ABI=
+# Machine's cache line size
+CONFIG_RTE_CACHE_LINE_SIZE=128
+# Memory model
+CONFIG_RTE_USE_C11_MEM_MODEL=n
+# Compile Environment Abstraction Layer
+CONFIG_RTE_LIBRTE_EAL=y
+CONFIG_RTE_MAX_LCORE=256
+CONFIG_RTE_MAX_NUMA_NODES=32
+CONFIG_RTE_MAX_HEAPS=32
+CONFIG_RTE_MAX_MEMSEG_LISTS=64
+# each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages
+# or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_LIST=8192
+CONFIG_RTE_MAX_MEM_MB_PER_LIST=32768
+# a "type" is a combination of page size and NUMA node. total number of memseg
+# lists per type will be limited to either RTE_MAX_MEMSEG_PER_TYPE pages (split
+# over multiple lists of RTE_MAX_MEMSEG_PER_LIST pages), or
+# RTE_MAX_MEM_MB_PER_TYPE megabytes of memory (split over multiple lists of
+# RTE_MAX_MEM_MB_PER_LIST), whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_TYPE=32768
+CONFIG_RTE_MAX_MEM_MB_PER_TYPE=131072
+# global maximum usable amount of VA, in megabytes
+CONFIG_RTE_MAX_MEM_MB=524288
+CONFIG_RTE_MAX_MEMZONE=2560
+CONFIG_RTE_MAX_TAILQ=32
+CONFIG_RTE_ENABLE_ASSERT=n
+CONFIG_RTE_LOG_DP_LEVEL=RTE_LOG_INFO
+CONFIG_RTE_LOG_HISTORY=256
+CONFIG_RTE_BACKTRACE=y
+CONFIG_RTE_LIBEAL_USE_HPET=n
+CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
+CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_EAL_IGB_UIO=n
+CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MAX_VFIO_GROUPS=64
+CONFIG_RTE_MAX_VFIO_CONTAINERS=64
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y
+CONFIG_RTE_USE_LIBBSD=n
+# Recognize/ignore architecture we compile for. AVX/AVX512 CPU flags for performance/power testing.
+# AVX512 is marked as experimental for now, will enable it after enough
+# field test and possible optimization.
+CONFIG_RTE_ENABLE_AVX=y
+CONFIG_RTE_ENABLE_AVX512=n
+# Default driver path (or "" to disable)
+CONFIG_RTE_EAL_PMD_PATH=""
+# Compile Environment Abstraction Layer to support Vmware TSC map
+CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=n
+# Compile architecture we compile for. PCI library
+CONFIG_RTE_LIBRTE_PCI=y
+# Compile architecture we compile for. argument parser library
+CONFIG_RTE_LIBRTE_KVARGS=y
+# Compile generic ethernet library
+CONFIG_RTE_LIBRTE_ETHER=y
+CONFIG_RTE_LIBRTE_ETHDEV_DEBUG=n
+CONFIG_RTE_MAX_ETHPORTS=32
+CONFIG_RTE_MAX_QUEUES_PER_PORT=1024
+CONFIG_RTE_LIBRTE_IEEE1588=n
+CONFIG_RTE_ETHDEV_QUEUE_STAT_CNTRS=16
+CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y
+CONFIG_RTE_ETHDEV_PROFILE_WITH_VTUNE=n
+# Turn off Tx preparation stage
+# Warning: rte_eth_tx_prepare() can be safely disabled only if using a
+# driver which do not implement any Tx preparation.
+CONFIG_RTE_ETHDEV_TX_PREPARE_NOOP=n
+# Common libraries, before Bus/PMDs
+CONFIG_RTE_LIBRTE_COMMON_DPAAX=n
+# Compile architecture we compile for. Intel FPGA bus
+CONFIG_RTE_LIBRTE_IFPGA_BUS=n
+# Compile PCI bus driver
+CONFIG_RTE_LIBRTE_PCI_BUS=y
+# Compile architecture we compile for. vdev bus
+CONFIG_RTE_LIBRTE_VDEV_BUS=y
+# Compile ARK PMD
+CONFIG_RTE_LIBRTE_ARK_PMD=n
+CONFIG_RTE_LIBRTE_ARK_PAD_TX=y
+CONFIG_RTE_LIBRTE_ARK_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_STATS=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TRACE=n
+# Compile Aquantia Atlantic PMD driver
+CONFIG_RTE_LIBRTE_ATLANTIC_PMD=n
+# Compile AMD PMD
+CONFIG_RTE_LIBRTE_AXGBE_PMD=n
+CONFIG_RTE_LIBRTE_AXGBE_PMD_DEBUG=n
+# Compile burst-oriented Broadcom PMD driver
+CONFIG_RTE_LIBRTE_BNX2X_PMD=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_BNX2X_MF_SUPPORT=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_PERIODIC=n
+# Compile burst-oriented Broadcom BNXT PMD driver
+CONFIG_RTE_LIBRTE_BNXT_PMD=n
+# Compile burst-oriented Chelsio Terminator (CXGBE) PMD
+CONFIG_RTE_LIBRTE_CXGBE_PMD=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_REG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_CXGBE_TPUT=y
+# NXP DPAA Bus
+CONFIG_RTE_LIBRTE_DPAA_BUS=n
+CONFIG_RTE_LIBRTE_DPAA_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA_PMD=n
+CONFIG_RTE_LIBRTE_DPAA_HWDEBUG=n
+# Compile NXP DPAA2 FSL-MC Bus
+CONFIG_RTE_LIBRTE_FSLMC_BUS=n
+# Compile Support Libraries for NXP DPAA2
+CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA2_USE_PHYS_IOVA=y
+# Compile burst-oriented NXP DPAA2 PMD driver
+CONFIG_RTE_LIBRTE_DPAA2_PMD=n
+CONFIG_RTE_LIBRTE_DPAA2_DEBUG_DRIVER=n
+# Compile NXP ENETC PMD Driver
+CONFIG_RTE_LIBRTE_ENETC_PMD=n
+# Compile burst-oriented Amazon ENA PMD driver
+CONFIG_RTE_LIBRTE_ENA_PMD=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_ENA_COM_DEBUG=n
+# Compile burst-oriented Cisco ENIC PMD driver
+CONFIG_RTE_LIBRTE_ENIC_PMD=n
+# Compile burst-oriented IGB & EM PMD drivers
+CONFIG_RTE_LIBRTE_EM_PMD=n
+CONFIG_RTE_LIBRTE_IGB_PMD=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_E1000_PF_DISABLE_STRIP_CRC=n
+# Compile burst-oriented IXGBE PMD driver
+CONFIG_RTE_LIBRTE_IXGBE_PMD=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_IXGBE_PF_DISABLE_STRIP_CRC=n
+CONFIG_RTE_IXGBE_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_IXGBE_BYPASS=n
+# Compile burst-oriented I40E PMD driver
+CONFIG_RTE_LIBRTE_I40E_PMD=y
+CONFIG_RTE_LIBRTE_I40E_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_I40E_RX_ALLOW_BULK_ALLOC=y
+CONFIG_RTE_LIBRTE_I40E_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC=n
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_PF=64
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM=4
+# Compile burst-oriented FM10K PMD
+CONFIG_RTE_LIBRTE_FM10K_PMD=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_FM10K_RX_OLFLAGS_ENABLE=y
+CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR=y
+# Compile burst-oriented AVF PMD driver
+CONFIG_RTE_LIBRTE_AVF_PMD=n
+CONFIG_RTE_LIBRTE_AVF_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVF_16BYTE_RX_DESC=n
+# Compile burst-oriented Mellanox ConnectX-3 (MLX4) PMD
+CONFIG_RTE_LIBRTE_MLX4_PMD=n
+CONFIG_RTE_LIBRTE_MLX4_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=n
+# Compile burst-oriented Mellanox ConnectX-4, ConnectX-5 & Bluefield
+# (MLX5) PMD
+CONFIG_RTE_LIBRTE_MLX5_PMD=n
+CONFIG_RTE_LIBRTE_MLX5_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS=n
+# Compile burst-oriented Netronome NFP PMD driver
+CONFIG_RTE_LIBRTE_NFP_PMD=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_RX=n
+# QLogic 10G/25G/40G/50G/100G PMD
+CONFIG_RTE_LIBRTE_QEDE_PMD=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_RX=n
+#Provides abs path/name of architecture we compile for. firmware file.
+#Empty string denotes driver will use default firmware
+CONFIG_RTE_LIBRTE_QEDE_FW=""
+# Compile burst-oriented Solarflare libefx-based PMD
+CONFIG_RTE_LIBRTE_SFC_EFX_PMD=n
+CONFIG_RTE_LIBRTE_SFC_EFX_DEBUG=n
+# Compile software PMD backed by SZEDATA2 device
+CONFIG_RTE_LIBRTE_PMD_SZEDATA2=n
+# Compile burst-oriented Cavium Thunderx NICVF PMD driver
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_TX=n
+# Compile burst-oriented Cavium LiquidIO PMD driver
+CONFIG_RTE_LIBRTE_LIO_PMD=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_REGS=n
+# Compile burst-oriented Cavium OCTEONTX network PMD driver
+CONFIG_RTE_LIBRTE_OCTEONTX_PMD=n
+# Compile WRS accelerated virtual port (AVP) guest PMD driver
+CONFIG_RTE_LIBRTE_AVP_PMD=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_BUFFERS=n
+# Compile burst-oriented VIRTIO PMD driver
+CONFIG_RTE_LIBRTE_VIRTIO_PMD=y
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DUMP=n
+# Compile virtio device emulation inside virtio PMD driver
+CONFIG_RTE_VIRTIO_USER=n
+# Compile burst-oriented VMXNET3 PMD driver
+CONFIG_RTE_LIBRTE_VMXNET3_PMD=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX_FREE=n
+# Compile software PMD backed by AF_PACKET sockets (Linux only)
+CONFIG_RTE_LIBRTE_PMD_AF_PACKET=n
+# Compile link bonding PMD library
+CONFIG_RTE_LIBRTE_PMD_BOND=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB_L1=n
+# Compile fail-safe PMD
+CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y
+# Compile Marvell PMD driver
+CONFIG_RTE_LIBRTE_MVPP2_PMD=n
+# Compile Marvell MVNETA PMD driver
+CONFIG_RTE_LIBRTE_MVNETA_PMD=n
+# Compile support for VMBus library
+CONFIG_RTE_LIBRTE_VMBUS=n
+# Compile native PMD for Hyper-V/Azure
+CONFIG_RTE_LIBRTE_NETVSC_PMD=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_DUMP=n
+# Compile virtual device driver for NetVSC on Hyper-V/Azure
+CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=n
+# Compile null PMD
+CONFIG_RTE_LIBRTE_PMD_NULL=n
+# Compile software PMD backed by PCAP files
+CONFIG_RTE_LIBRTE_PMD_PCAP=n
+# Compile example software rings based PMD
+CONFIG_RTE_LIBRTE_PMD_RING=y
+CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16
+CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16
+# Compile SOFTNIC PMD
+CONFIG_RTE_LIBRTE_PMD_SOFTNIC=n
+# Compile architecture we compile for. TAP PMD
+# It is enabled by default for Linux only.
+CONFIG_RTE_LIBRTE_PMD_TAP=y
+# Do prefetch of packet data within PMD driver receive function
+CONFIG_RTE_PMD_PACKET_PREFETCH=y
+# Compile generic wireless base band device library
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_BBDEV=n
+CONFIG_RTE_BBDEV_MAX_DEVS=128
+CONFIG_RTE_BBDEV_OFFLOAD_COST=n
+# Compile PMD for NULL bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL=y
+# Compile PMD for turbo software bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_TURBO_SW=n
+# Compile generic crypto device library
+CONFIG_RTE_LIBRTE_CRYPTODEV=n
+CONFIG_RTE_CRYPTO_MAX_DEVS=64
+# Compile PMD for ARMv8 Crypto device
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO=n
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO_DEBUG=n
+# Compile NXP CAAM JR crypto Driver
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR=n
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE=n
+# Compile NXP DPAA2 crypto sec driver for CAAM HW
+CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC=n
+# NXP DPAA caam - crypto driver
+CONFIG_RTE_LIBRTE_PMD_DPAA_SEC=n
+CONFIG_RTE_LIBRTE_DPAA_MAX_CRYPTODEV=4
+# Compile PMD for Cavium OCTEON TX crypto device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_CRYPTO=y
+# Compile PMD for QuickAssist based devices - see docs for details
+CONFIG_RTE_LIBRTE_PMD_QAT=n
+CONFIG_RTE_LIBRTE_PMD_QAT_SYM=n
+# Max. number of QuickAssist devices, which can be detected and attached
+CONFIG_RTE_PMD_QAT_MAX_PCI_DEVICES=48
+CONFIG_RTE_PMD_QAT_COMP_SGL_MAX_SEGMENTS=16
+CONFIG_RTE_PMD_QAT_COMP_IM_BUFFER_SIZE=65536
+# Compile PMD for virtio crypto devices
+CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO=n
+# Number of maximum virtio crypto devices
+CONFIG_RTE_MAX_VIRTIO_CRYPTO=32
+# Compile PMD for AESNI backed device
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB=n
+# Compile PMD for Software backed device
+CONFIG_RTE_LIBRTE_PMD_OPENSSL=n
+# Compile PMD for AESNI GCM device
+CONFIG_RTE_LIBRTE_PMD_AESNI_GCM=n
+# Compile PMD for SNOW 3G device
+CONFIG_RTE_LIBRTE_PMD_SNOW3G=n
+CONFIG_RTE_LIBRTE_PMD_SNOW3G_DEBUG=n
+# Compile PMD for KASUMI device
+CONFIG_RTE_LIBRTE_PMD_KASUMI=n
+# Compile PMD for ZUC device
+CONFIG_RTE_LIBRTE_PMD_ZUC=n
+# Compile PMD for Crypto Scheduler device
+CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER=n
+# Compile PMD for NULL Crypto device
+CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO=n
+# Compile PMD for AMD CCP crypto device
+CONFIG_RTE_LIBRTE_PMD_CCP=n
+# Compile PMD for Marvell Crypto device
+CONFIG_RTE_LIBRTE_PMD_MVSAM_CRYPTO=n
+# Compile generic security library
+CONFIG_RTE_LIBRTE_SECURITY=n
+# Compile generic compression device library
+CONFIG_RTE_LIBRTE_COMPRESSDEV=n
+CONFIG_RTE_COMPRESS_MAX_DEVS=64
+# Compile compressdev unit test
+CONFIG_RTE_COMPRESSDEV_TEST=n
+# Compile PMD for Octeontx ZIPVF compression device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF=n
+# Compile PMD for ISA-L compression device
+CONFIG_RTE_LIBRTE_PMD_ISAL=n
+# Compile PMD for ZLIB compression device
+CONFIG_RTE_LIBRTE_PMD_ZLIB=n
+# Compile generic event device library
+CONFIG_RTE_LIBRTE_EVENTDEV=n
+CONFIG_RTE_LIBRTE_EVENTDEV_DEBUG=n
+CONFIG_RTE_EVENT_MAX_DEVS=16
+CONFIG_RTE_EVENT_MAX_QUEUES_PER_DEV=64
+CONFIG_RTE_EVENT_TIMER_ADAPTER_NUM_MAX=32
+CONFIG_RTE_EVENT_ETH_INTR_RING_SIZE=1024
+CONFIG_RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE=32
+CONFIG_RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE=32
+# Compile PMD for skeleton event device
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV=n
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV_DEBUG=n
+# Compile PMD for software event device
+CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV=n
+# Compile PMD for distributed software event device
+CONFIG_RTE_LIBRTE_PMD_DSW_EVENTDEV=n
+# Compile PMD for octeontx sso event device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF=n
+# Compile PMD for OPDL event device
+CONFIG_RTE_LIBRTE_PMD_OPDL_EVENTDEV=n
+# Compile PMD for NXP DPAA event device
+CONFIG_RTE_LIBRTE_PMD_DPAA_EVENTDEV=n
+# Compile PMD for NXP DPAA2 event device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_EVENTDEV=n
+# Compile raw device support
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_RAWDEV=n
+CONFIG_RTE_RAWDEV_MAX_DEVS=10
+CONFIG_RTE_LIBRTE_PMD_SKELETON_RAWDEV=n
+# Compile PMD for NXP DPAA2 CMDIF raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_CMDIF_RAWDEV=n
+# Compile PMD for NXP DPAA2 QDMA raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=n
+# Compile PMD for Intel FPGA raw device
+CONFIG_RTE_LIBRTE_PMD_IFPGA_RAWDEV=n
+# Compile librte_ring
+CONFIG_RTE_LIBRTE_RING=y
+# Compile librte_mempool
+CONFIG_RTE_LIBRTE_MEMPOOL=y
+CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=512
+CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n
+# Compile Mempool drivers
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET=y
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB=64
+CONFIG_RTE_DRIVER_MEMPOOL_RING=y
+CONFIG_RTE_DRIVER_MEMPOOL_STACK=y
+# Compile PMD for octeontx fpa mempool device
+CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL=n
+# Compile librte_mbuf
+CONFIG_RTE_LIBRTE_MBUF=y
+CONFIG_RTE_LIBRTE_MBUF_DEBUG=n
+CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc"
+CONFIG_RTE_MBUF_REFCNT_ATOMIC=y
+CONFIG_RTE_PKTMBUF_HEADROOM=128
+# Compile librte_timer
+CONFIG_RTE_LIBRTE_TIMER=n
+CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
+# Compile librte_cfgfile
+CONFIG_RTE_LIBRTE_CFGFILE=n
+# Compile librte_cmdline
+CONFIG_RTE_LIBRTE_CMDLINE=y
+CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n
+# Compile librte_hash
+CONFIG_RTE_LIBRTE_HASH=y
+CONFIG_RTE_LIBRTE_HASH_DEBUG=n
+# Compile librte_efd
+CONFIG_RTE_LIBRTE_EFD=n
+# Compile librte_member
+CONFIG_RTE_LIBRTE_MEMBER=y
+# Compile librte_jobstats
+CONFIG_RTE_LIBRTE_JOBSTATS=n
+# Compile architecture we compile for. device metrics library
+CONFIG_RTE_LIBRTE_METRICS=y
+# Compile architecture we compile for. bitrate statistics library
+CONFIG_RTE_LIBRTE_BITRATE=y
+# Compile architecture we compile for. latency statistics library
+CONFIG_RTE_LIBRTE_LATENCY_STATS=y
+# Compile librte_telemetry
+CONFIG_RTE_LIBRTE_TELEMETRY=n
+# Compile librte_lpm
+CONFIG_RTE_LIBRTE_LPM=n
+CONFIG_RTE_LIBRTE_LPM_DEBUG=n
+# Compile librte_acl
+CONFIG_RTE_LIBRTE_ACL=n
+CONFIG_RTE_LIBRTE_ACL_DEBUG=n
+# Compile librte_power
+CONFIG_RTE_LIBRTE_POWER=n
+CONFIG_RTE_LIBRTE_POWER_DEBUG=n
+CONFIG_RTE_MAX_LCORE_FREQS=64
+# Compile librte_net
+CONFIG_RTE_LIBRTE_NET=y
+# Compile librte_ip_frag
+CONFIG_RTE_LIBRTE_IP_FRAG=y
+CONFIG_RTE_LIBRTE_IP_FRAG_DEBUG=n
+CONFIG_RTE_LIBRTE_IP_FRAG_MAX_FRAG=4
+CONFIG_RTE_LIBRTE_IP_FRAG_TBL_STAT=n
+# Compile GRO library
+CONFIG_RTE_LIBRTE_GRO=y
+# Compile GSO library
+CONFIG_RTE_LIBRTE_GSO=y
+# Compile librte_meter
+CONFIG_RTE_LIBRTE_METER=y
+# Compile librte_classify
+CONFIG_RTE_LIBRTE_FLOW_CLASSIFY=n
+# Compile librte_sched
+CONFIG_RTE_LIBRTE_SCHED=n
+CONFIG_RTE_SCHED_DEBUG=n
+CONFIG_RTE_SCHED_RED=n
+CONFIG_RTE_SCHED_COLLECT_STATS=n
+CONFIG_RTE_SCHED_SUBPORT_TC_OV=n
+CONFIG_RTE_SCHED_PORT_N_GRINDERS=8
+CONFIG_RTE_SCHED_VECTOR=n
+# Compile architecture we compile for. distributor library
+CONFIG_RTE_LIBRTE_DISTRIBUTOR=n
+# Compile architecture we compile for. reorder library
+CONFIG_RTE_LIBRTE_REORDER=n
+# Compile librte_port
+CONFIG_RTE_LIBRTE_PORT=n
+CONFIG_RTE_PORT_STATS_COLLECT=n
+CONFIG_RTE_PORT_PCAP=n
+# Compile librte_table
+CONFIG_RTE_LIBRTE_TABLE=n
+CONFIG_RTE_TABLE_STATS_COLLECT=n
+# Compile librte_pipeline
+CONFIG_RTE_LIBRTE_PIPELINE=n
+CONFIG_RTE_PIPELINE_STATS_COLLECT=n
+# Compile librte_kni
+CONFIG_RTE_LIBRTE_KNI=n
+CONFIG_RTE_LIBRTE_PMD_KNI=n
+CONFIG_RTE_KNI_KMOD=n
+CONFIG_RTE_KNI_KMOD_ETHTOOL=n
+CONFIG_RTE_KNI_PREEMPT_DEFAULT=y
+# Compile architecture we compile for. pdump library
+CONFIG_RTE_LIBRTE_PDUMP=y
+# Compile vhost user library
+CONFIG_RTE_LIBRTE_VHOST=y
+CONFIG_RTE_LIBRTE_VHOST_NUMA=y
+CONFIG_RTE_LIBRTE_VHOST_DEBUG=n
+# Compile vhost PMD
+# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled.
+CONFIG_RTE_LIBRTE_PMD_VHOST=y
+# Compile IFC driver
+# To compile, CONFIG_RTE_LIBRTE_VHOST and CONFIG_RTE_EAL_VFIO
+# should be enabled.
+CONFIG_RTE_LIBRTE_IFC_PMD=n
+# Compile librte_bpf
+CONFIG_RTE_LIBRTE_BPF=n
+# allow load BPF from ELF files (requires libelf)
+CONFIG_RTE_LIBRTE_BPF_ELF=n
+# Compile architecture we compile for. test application
+CONFIG_RTE_APP_TEST=y
+CONFIG_RTE_APP_TEST_RESOURCE_TAR=n
+# Compile architecture we compile for. procinfo application
+CONFIG_RTE_PROC_INFO=y
+# Compile architecture we compile for. PMD test application
+CONFIG_RTE_TEST_PMD=n
+CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n
+CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n
+# Compile architecture we compile for. bbdev test application
+CONFIG_RTE_TEST_BBDEV=n
+# Compile architecture we compile for. crypto performance application
+CONFIG_RTE_APP_CRYPTO_PERF=n
+# Compile architecture we compile for. eventdev application
+CONFIG_RTE_APP_EVENTDEV=n
+CONFIG_RTE_EXEC_ENV_LINUXAPP=y
+CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n
+# Common libraries, before Bus/PMDs
+# NXP DPAA BUS and drivers
+# NXP FSLMC BUS and DPAA2 drivers
+# NXP ENETC PMD Driver
+CONFIG_RTE_ARCH_PPC_64=y
+CONFIG_RTE_ARCH_64=y
+CONFIG_RTE_TOOLCHAIN_GCC=y
+# Note: Power doesn't have this support
+# Note: Initially, all of architecture we compile for. PMD drivers compilation are turned off on Power
+# Will turn on them only after architecture we compile for. successful testing on Power
+CONFIG_RTE_LIBRTE_PMD_XENVIRT=n
diff --git a/SOURCES/set_config.sh b/SOURCES/set_config.sh
new file mode 100755
index 0000000..002386b
--- /dev/null
+++ b/SOURCES/set_config.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+# Copyright (C) 2017, Red Hat, Inc.
+#
+# set_config.sh will copy a configuration from $1 to $2, in the process
+# checking that the sha header for $1 matches the header in $2
+
+source configlib.sh
+
+if (( $# < 2 )); then
+    echo "$0: source dest [comment-marker]"
+    exit 1
+fi
+
+if [ ! -f "$1" ]; then
+    echo "Source file $1 must exist."
+    exit 1
+fi
+src_file=$1
+shift
+
+if [ ! -f "$1" ]; then
+    echo "Dest file $1 must exist."
+    exit 1
+fi
+dst_file=$1
+shift
+
+comment_sep=${1:-#}
+
+export LANG=en_US.utf8
+
+DEST_FILE_SHA=""
+SRC_FILE_SHA=""
+
+calc_sha DEST_FILE_SHA "$dst_file" "$comment_sep" || echo "Failed to calc sha"
+retr_sha SRC_FILE_SHA "$src_file" "$comment_sep" || echo "Failed to retrieve sha"
+
+if [ "$DEST_FILE_SHA" != "$SRC_FILE_SHA" ]; then
+    echo "ERROR: The requisite starting sha from $dst_file does not match the"
+    echo "       specified sha in $src_file."
+    echo "[ $DEST_FILE_SHA ] vs [ $SRC_FILE_SHA ]"
+    exit 1
+fi
+
+mv "$dst_file" "$dst_file".OLD
+cp "$src_file" "$dst_file"
+echo "copied 1 config file."
+exit 0
diff --git a/SOURCES/x86_64-native-linuxapp-gcc-config b/SOURCES/x86_64-native-linuxapp-gcc-config
new file mode 100644
index 0000000..4b7a7ea
--- /dev/null
+++ b/SOURCES/x86_64-native-linuxapp-gcc-config
@@ -0,0 +1,525 @@
+# -*- cfg-sha: 2ba93102021dc5d38494cf5090c3ecaca37db13153dd558b1511a56f2a3d9b10
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2014 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2016 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2017 Intel Corporation
+# RTE_EXEC_ENV values are the directories in mk/exec-env/
+CONFIG_RTE_EXEC_ENV="linuxapp"
+# RTE_ARCH values are architecture we compile for. directories in mk/arch/
+CONFIG_RTE_ARCH="x86_64"
+# machine can define specific variables or action for a specific board
+# RTE_MACHINE values are architecture we compile for. directories in mk/machine/
+CONFIG_RTE_MACHINE="default"
+# The compiler we use.
+# RTE_TOOLCHAIN values are architecture we compile for. directories in mk/toolchain/
+CONFIG_RTE_TOOLCHAIN="gcc"
+# Use intrinsics or assembly code for key routines
+CONFIG_RTE_FORCE_INTRINSICS=n
+# Machine forces strict alignment constraints.
+CONFIG_RTE_ARCH_STRICT_ALIGN=n
+# Compile to share library
+CONFIG_RTE_BUILD_SHARED_LIB=n
+# Use newest code breaking previous ABI
+CONFIG_RTE_NEXT_ABI=n
+# Major ABI to overwrite library specific LIBABIVER
+CONFIG_RTE_MAJOR_ABI=
+# Machine's cache line size
+CONFIG_RTE_CACHE_LINE_SIZE=64
+# Memory model
+CONFIG_RTE_USE_C11_MEM_MODEL=n
+# Compile Environment Abstraction Layer
+CONFIG_RTE_LIBRTE_EAL=y
+CONFIG_RTE_MAX_LCORE=128
+CONFIG_RTE_MAX_NUMA_NODES=8
+CONFIG_RTE_MAX_HEAPS=32
+CONFIG_RTE_MAX_MEMSEG_LISTS=64
+# each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages
+# or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_LIST=8192
+CONFIG_RTE_MAX_MEM_MB_PER_LIST=32768
+# a "type" is a combination of page size and NUMA node. total number of memseg
+# lists per type will be limited to either RTE_MAX_MEMSEG_PER_TYPE pages (split
+# over multiple lists of RTE_MAX_MEMSEG_PER_LIST pages), or
+# RTE_MAX_MEM_MB_PER_TYPE megabytes of memory (split over multiple lists of
+# RTE_MAX_MEM_MB_PER_LIST), whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_TYPE=32768
+CONFIG_RTE_MAX_MEM_MB_PER_TYPE=131072
+# global maximum usable amount of VA, in megabytes
+CONFIG_RTE_MAX_MEM_MB=524288
+CONFIG_RTE_MAX_MEMZONE=2560
+CONFIG_RTE_MAX_TAILQ=32
+CONFIG_RTE_ENABLE_ASSERT=n
+CONFIG_RTE_LOG_DP_LEVEL=RTE_LOG_INFO
+CONFIG_RTE_LOG_HISTORY=256
+CONFIG_RTE_BACKTRACE=y
+CONFIG_RTE_LIBEAL_USE_HPET=n
+CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
+CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_EAL_IGB_UIO=n
+CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MAX_VFIO_GROUPS=64
+CONFIG_RTE_MAX_VFIO_CONTAINERS=64
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y
+CONFIG_RTE_USE_LIBBSD=n
+# Recognize/ignore architecture we compile for. AVX/AVX512 CPU flags for performance/power testing.
+# AVX512 is marked as experimental for now, will enable it after enough
+# field test and possible optimization.
+CONFIG_RTE_ENABLE_AVX=y
+CONFIG_RTE_ENABLE_AVX512=n
+# Default driver path (or "" to disable)
+CONFIG_RTE_EAL_PMD_PATH=""
+# Compile Environment Abstraction Layer to support Vmware TSC map
+CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=y
+# Compile architecture we compile for. PCI library
+CONFIG_RTE_LIBRTE_PCI=y
+# Compile architecture we compile for. argument parser library
+CONFIG_RTE_LIBRTE_KVARGS=y
+# Compile generic ethernet library
+CONFIG_RTE_LIBRTE_ETHER=y
+CONFIG_RTE_LIBRTE_ETHDEV_DEBUG=n
+CONFIG_RTE_MAX_ETHPORTS=32
+CONFIG_RTE_MAX_QUEUES_PER_PORT=1024
+CONFIG_RTE_LIBRTE_IEEE1588=n
+CONFIG_RTE_ETHDEV_QUEUE_STAT_CNTRS=16
+CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y
+CONFIG_RTE_ETHDEV_PROFILE_WITH_VTUNE=n
+# Turn off Tx preparation stage
+# Warning: rte_eth_tx_prepare() can be safely disabled only if using a
+# driver which do not implement any Tx preparation.
+CONFIG_RTE_ETHDEV_TX_PREPARE_NOOP=n
+# Common libraries, before Bus/PMDs
+CONFIG_RTE_LIBRTE_COMMON_DPAAX=n
+# Compile architecture we compile for. Intel FPGA bus
+CONFIG_RTE_LIBRTE_IFPGA_BUS=n
+# Compile PCI bus driver
+CONFIG_RTE_LIBRTE_PCI_BUS=y
+# Compile architecture we compile for. vdev bus
+CONFIG_RTE_LIBRTE_VDEV_BUS=y
+# Compile ARK PMD
+CONFIG_RTE_LIBRTE_ARK_PMD=n
+CONFIG_RTE_LIBRTE_ARK_PAD_TX=y
+CONFIG_RTE_LIBRTE_ARK_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_STATS=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TRACE=n
+# Compile Aquantia Atlantic PMD driver
+CONFIG_RTE_LIBRTE_ATLANTIC_PMD=n
+# Compile AMD PMD
+CONFIG_RTE_LIBRTE_AXGBE_PMD=n
+CONFIG_RTE_LIBRTE_AXGBE_PMD_DEBUG=n
+# Compile burst-oriented Broadcom PMD driver
+CONFIG_RTE_LIBRTE_BNX2X_PMD=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_BNX2X_MF_SUPPORT=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_PERIODIC=n
+# Compile burst-oriented Broadcom BNXT PMD driver
+CONFIG_RTE_LIBRTE_BNXT_PMD=y
+# Compile burst-oriented Chelsio Terminator (CXGBE) PMD
+CONFIG_RTE_LIBRTE_CXGBE_PMD=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_REG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_CXGBE_TPUT=y
+# NXP DPAA Bus
+CONFIG_RTE_LIBRTE_DPAA_BUS=n
+CONFIG_RTE_LIBRTE_DPAA_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA_PMD=n
+CONFIG_RTE_LIBRTE_DPAA_HWDEBUG=n
+# Compile NXP DPAA2 FSL-MC Bus
+CONFIG_RTE_LIBRTE_FSLMC_BUS=n
+# Compile Support Libraries for NXP DPAA2
+CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA2_USE_PHYS_IOVA=y
+# Compile burst-oriented NXP DPAA2 PMD driver
+CONFIG_RTE_LIBRTE_DPAA2_PMD=n
+CONFIG_RTE_LIBRTE_DPAA2_DEBUG_DRIVER=n
+# Compile NXP ENETC PMD Driver
+CONFIG_RTE_LIBRTE_ENETC_PMD=n
+# Compile burst-oriented Amazon ENA PMD driver
+CONFIG_RTE_LIBRTE_ENA_PMD=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_ENA_COM_DEBUG=n
+# Compile burst-oriented Cisco ENIC PMD driver
+CONFIG_RTE_LIBRTE_ENIC_PMD=y
+# Compile burst-oriented IGB & EM PMD drivers
+CONFIG_RTE_LIBRTE_EM_PMD=n
+CONFIG_RTE_LIBRTE_IGB_PMD=y
+CONFIG_RTE_LIBRTE_E1000_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_E1000_PF_DISABLE_STRIP_CRC=n
+# Compile burst-oriented IXGBE PMD driver
+CONFIG_RTE_LIBRTE_IXGBE_PMD=y
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_IXGBE_PF_DISABLE_STRIP_CRC=n
+CONFIG_RTE_IXGBE_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_IXGBE_BYPASS=n
+# Compile burst-oriented I40E PMD driver
+CONFIG_RTE_LIBRTE_I40E_PMD=y
+CONFIG_RTE_LIBRTE_I40E_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_I40E_RX_ALLOW_BULK_ALLOC=y
+CONFIG_RTE_LIBRTE_I40E_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC=n
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_PF=64
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM=4
+# Compile burst-oriented FM10K PMD
+CONFIG_RTE_LIBRTE_FM10K_PMD=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_FM10K_RX_OLFLAGS_ENABLE=y
+CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR=y
+# Compile burst-oriented AVF PMD driver
+CONFIG_RTE_LIBRTE_AVF_PMD=n
+CONFIG_RTE_LIBRTE_AVF_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVF_16BYTE_RX_DESC=n
+# Compile burst-oriented Mellanox ConnectX-3 (MLX4) PMD
+CONFIG_RTE_LIBRTE_MLX4_PMD=y
+CONFIG_RTE_LIBRTE_MLX4_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=y
+# Compile burst-oriented Mellanox ConnectX-4, ConnectX-5 & Bluefield
+# (MLX5) PMD
+CONFIG_RTE_LIBRTE_MLX5_PMD=y
+CONFIG_RTE_LIBRTE_MLX5_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS=y
+# Compile burst-oriented Netronome NFP PMD driver
+CONFIG_RTE_LIBRTE_NFP_PMD=y
+CONFIG_RTE_LIBRTE_NFP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_RX=n
+# QLogic 10G/25G/40G/50G/100G PMD
+CONFIG_RTE_LIBRTE_QEDE_PMD=y
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_RX=n
+#Provides abs path/name of architecture we compile for. firmware file.
+#Empty string denotes driver will use default firmware
+CONFIG_RTE_LIBRTE_QEDE_FW=""
+# Compile burst-oriented Solarflare libefx-based PMD
+CONFIG_RTE_LIBRTE_SFC_EFX_PMD=n
+CONFIG_RTE_LIBRTE_SFC_EFX_DEBUG=n
+# Compile software PMD backed by SZEDATA2 device
+CONFIG_RTE_LIBRTE_PMD_SZEDATA2=n
+# Compile burst-oriented Cavium Thunderx NICVF PMD driver
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_TX=n
+# Compile burst-oriented Cavium LiquidIO PMD driver
+CONFIG_RTE_LIBRTE_LIO_PMD=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_REGS=n
+# Compile burst-oriented Cavium OCTEONTX network PMD driver
+CONFIG_RTE_LIBRTE_OCTEONTX_PMD=n
+# Compile WRS accelerated virtual port (AVP) guest PMD driver
+CONFIG_RTE_LIBRTE_AVP_PMD=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_BUFFERS=n
+# Compile burst-oriented VIRTIO PMD driver
+CONFIG_RTE_LIBRTE_VIRTIO_PMD=y
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DUMP=n
+# Compile virtio device emulation inside virtio PMD driver
+CONFIG_RTE_VIRTIO_USER=n
+# Compile burst-oriented VMXNET3 PMD driver
+CONFIG_RTE_LIBRTE_VMXNET3_PMD=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX_FREE=n
+# Compile software PMD backed by AF_PACKET sockets (Linux only)
+CONFIG_RTE_LIBRTE_PMD_AF_PACKET=n
+# Compile link bonding PMD library
+CONFIG_RTE_LIBRTE_PMD_BOND=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB_L1=n
+# Compile fail-safe PMD
+CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y
+# Compile Marvell PMD driver
+CONFIG_RTE_LIBRTE_MVPP2_PMD=n
+# Compile Marvell MVNETA PMD driver
+CONFIG_RTE_LIBRTE_MVNETA_PMD=n
+# Compile support for VMBus library
+CONFIG_RTE_LIBRTE_VMBUS=y
+# Compile native PMD for Hyper-V/Azure
+CONFIG_RTE_LIBRTE_NETVSC_PMD=y
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_DUMP=n
+# Compile virtual device driver for NetVSC on Hyper-V/Azure
+CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=y
+# Compile null PMD
+CONFIG_RTE_LIBRTE_PMD_NULL=n
+# Compile software PMD backed by PCAP files
+CONFIG_RTE_LIBRTE_PMD_PCAP=n
+# Compile example software rings based PMD
+CONFIG_RTE_LIBRTE_PMD_RING=y
+CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16
+CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16
+# Compile SOFTNIC PMD
+CONFIG_RTE_LIBRTE_PMD_SOFTNIC=n
+# Compile architecture we compile for. TAP PMD
+# It is enabled by default for Linux only.
+CONFIG_RTE_LIBRTE_PMD_TAP=y
+# Do prefetch of packet data within PMD driver receive function
+CONFIG_RTE_PMD_PACKET_PREFETCH=y
+# Compile generic wireless base band device library
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_BBDEV=n
+CONFIG_RTE_BBDEV_MAX_DEVS=128
+CONFIG_RTE_BBDEV_OFFLOAD_COST=n
+# Compile PMD for NULL bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL=y
+# Compile PMD for turbo software bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_TURBO_SW=n
+# Compile generic crypto device library
+CONFIG_RTE_LIBRTE_CRYPTODEV=n
+CONFIG_RTE_CRYPTO_MAX_DEVS=64
+# Compile PMD for ARMv8 Crypto device
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO=n
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO_DEBUG=n
+# Compile NXP CAAM JR crypto Driver
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR=n
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE=n
+# Compile NXP DPAA2 crypto sec driver for CAAM HW
+CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC=n
+# NXP DPAA caam - crypto driver
+CONFIG_RTE_LIBRTE_PMD_DPAA_SEC=n
+CONFIG_RTE_LIBRTE_DPAA_MAX_CRYPTODEV=4
+# Compile PMD for Cavium OCTEON TX crypto device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_CRYPTO=y
+# Compile PMD for QuickAssist based devices - see docs for details
+CONFIG_RTE_LIBRTE_PMD_QAT=n
+CONFIG_RTE_LIBRTE_PMD_QAT_SYM=n
+# Max. number of QuickAssist devices, which can be detected and attached
+CONFIG_RTE_PMD_QAT_MAX_PCI_DEVICES=48
+CONFIG_RTE_PMD_QAT_COMP_SGL_MAX_SEGMENTS=16
+CONFIG_RTE_PMD_QAT_COMP_IM_BUFFER_SIZE=65536
+# Compile PMD for virtio crypto devices
+CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO=n
+# Number of maximum virtio crypto devices
+CONFIG_RTE_MAX_VIRTIO_CRYPTO=32
+# Compile PMD for AESNI backed device
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB=n
+# Compile PMD for Software backed device
+CONFIG_RTE_LIBRTE_PMD_OPENSSL=n
+# Compile PMD for AESNI GCM device
+CONFIG_RTE_LIBRTE_PMD_AESNI_GCM=n
+# Compile PMD for SNOW 3G device
+CONFIG_RTE_LIBRTE_PMD_SNOW3G=n
+CONFIG_RTE_LIBRTE_PMD_SNOW3G_DEBUG=n
+# Compile PMD for KASUMI device
+CONFIG_RTE_LIBRTE_PMD_KASUMI=n
+# Compile PMD for ZUC device
+CONFIG_RTE_LIBRTE_PMD_ZUC=n
+# Compile PMD for Crypto Scheduler device
+CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER=n
+# Compile PMD for NULL Crypto device
+CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO=n
+# Compile PMD for AMD CCP crypto device
+CONFIG_RTE_LIBRTE_PMD_CCP=n
+# Compile PMD for Marvell Crypto device
+CONFIG_RTE_LIBRTE_PMD_MVSAM_CRYPTO=n
+# Compile generic security library
+CONFIG_RTE_LIBRTE_SECURITY=n
+# Compile generic compression device library
+CONFIG_RTE_LIBRTE_COMPRESSDEV=n
+CONFIG_RTE_COMPRESS_MAX_DEVS=64
+# Compile compressdev unit test
+CONFIG_RTE_COMPRESSDEV_TEST=n
+# Compile PMD for Octeontx ZIPVF compression device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF=n
+# Compile PMD for ISA-L compression device
+CONFIG_RTE_LIBRTE_PMD_ISAL=n
+# Compile PMD for ZLIB compression device
+CONFIG_RTE_LIBRTE_PMD_ZLIB=n
+# Compile generic event device library
+CONFIG_RTE_LIBRTE_EVENTDEV=n
+CONFIG_RTE_LIBRTE_EVENTDEV_DEBUG=n
+CONFIG_RTE_EVENT_MAX_DEVS=16
+CONFIG_RTE_EVENT_MAX_QUEUES_PER_DEV=64
+CONFIG_RTE_EVENT_TIMER_ADAPTER_NUM_MAX=32
+CONFIG_RTE_EVENT_ETH_INTR_RING_SIZE=1024
+CONFIG_RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE=32
+CONFIG_RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE=32
+# Compile PMD for skeleton event device
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV=n
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV_DEBUG=n
+# Compile PMD for software event device
+CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV=n
+# Compile PMD for distributed software event device
+CONFIG_RTE_LIBRTE_PMD_DSW_EVENTDEV=n
+# Compile PMD for octeontx sso event device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF=n
+# Compile PMD for OPDL event device
+CONFIG_RTE_LIBRTE_PMD_OPDL_EVENTDEV=n
+# Compile PMD for NXP DPAA event device
+CONFIG_RTE_LIBRTE_PMD_DPAA_EVENTDEV=n
+# Compile PMD for NXP DPAA2 event device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_EVENTDEV=n
+# Compile raw device support
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_RAWDEV=n
+CONFIG_RTE_RAWDEV_MAX_DEVS=10
+CONFIG_RTE_LIBRTE_PMD_SKELETON_RAWDEV=n
+# Compile PMD for NXP DPAA2 CMDIF raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_CMDIF_RAWDEV=n
+# Compile PMD for NXP DPAA2 QDMA raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=n
+# Compile PMD for Intel FPGA raw device
+CONFIG_RTE_LIBRTE_PMD_IFPGA_RAWDEV=n
+# Compile librte_ring
+CONFIG_RTE_LIBRTE_RING=y
+# Compile librte_mempool
+CONFIG_RTE_LIBRTE_MEMPOOL=y
+CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=512
+CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n
+# Compile Mempool drivers
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET=y
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB=64
+CONFIG_RTE_DRIVER_MEMPOOL_RING=y
+CONFIG_RTE_DRIVER_MEMPOOL_STACK=y
+# Compile PMD for octeontx fpa mempool device
+CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL=n
+# Compile librte_mbuf
+CONFIG_RTE_LIBRTE_MBUF=y
+CONFIG_RTE_LIBRTE_MBUF_DEBUG=n
+CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc"
+CONFIG_RTE_MBUF_REFCNT_ATOMIC=y
+CONFIG_RTE_PKTMBUF_HEADROOM=128
+# Compile librte_timer
+CONFIG_RTE_LIBRTE_TIMER=n
+CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
+# Compile librte_cfgfile
+CONFIG_RTE_LIBRTE_CFGFILE=n
+# Compile librte_cmdline
+CONFIG_RTE_LIBRTE_CMDLINE=y
+CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n
+# Compile librte_hash
+CONFIG_RTE_LIBRTE_HASH=y
+CONFIG_RTE_LIBRTE_HASH_DEBUG=n
+# Compile librte_efd
+CONFIG_RTE_LIBRTE_EFD=n
+# Compile librte_member
+CONFIG_RTE_LIBRTE_MEMBER=y
+# Compile librte_jobstats
+CONFIG_RTE_LIBRTE_JOBSTATS=n
+# Compile architecture we compile for. device metrics library
+CONFIG_RTE_LIBRTE_METRICS=y
+# Compile architecture we compile for. bitrate statistics library
+CONFIG_RTE_LIBRTE_BITRATE=y
+# Compile architecture we compile for. latency statistics library
+CONFIG_RTE_LIBRTE_LATENCY_STATS=y
+# Compile librte_telemetry
+CONFIG_RTE_LIBRTE_TELEMETRY=n
+# Compile librte_lpm
+CONFIG_RTE_LIBRTE_LPM=n
+CONFIG_RTE_LIBRTE_LPM_DEBUG=n
+# Compile librte_acl
+CONFIG_RTE_LIBRTE_ACL=n
+CONFIG_RTE_LIBRTE_ACL_DEBUG=n
+# Compile librte_power
+CONFIG_RTE_LIBRTE_POWER=n
+CONFIG_RTE_LIBRTE_POWER_DEBUG=n
+CONFIG_RTE_MAX_LCORE_FREQS=64
+# Compile librte_net
+CONFIG_RTE_LIBRTE_NET=y
+# Compile librte_ip_frag
+CONFIG_RTE_LIBRTE_IP_FRAG=y
+CONFIG_RTE_LIBRTE_IP_FRAG_DEBUG=n
+CONFIG_RTE_LIBRTE_IP_FRAG_MAX_FRAG=4
+CONFIG_RTE_LIBRTE_IP_FRAG_TBL_STAT=n
+# Compile GRO library
+CONFIG_RTE_LIBRTE_GRO=y
+# Compile GSO library
+CONFIG_RTE_LIBRTE_GSO=y
+# Compile librte_meter
+CONFIG_RTE_LIBRTE_METER=y
+# Compile librte_classify
+CONFIG_RTE_LIBRTE_FLOW_CLASSIFY=n
+# Compile librte_sched
+CONFIG_RTE_LIBRTE_SCHED=n
+CONFIG_RTE_SCHED_DEBUG=n
+CONFIG_RTE_SCHED_RED=n
+CONFIG_RTE_SCHED_COLLECT_STATS=n
+CONFIG_RTE_SCHED_SUBPORT_TC_OV=n
+CONFIG_RTE_SCHED_PORT_N_GRINDERS=8
+CONFIG_RTE_SCHED_VECTOR=n
+# Compile architecture we compile for. distributor library
+CONFIG_RTE_LIBRTE_DISTRIBUTOR=n
+# Compile architecture we compile for. reorder library
+CONFIG_RTE_LIBRTE_REORDER=n
+# Compile librte_port
+CONFIG_RTE_LIBRTE_PORT=n
+CONFIG_RTE_PORT_STATS_COLLECT=n
+CONFIG_RTE_PORT_PCAP=n
+# Compile librte_table
+CONFIG_RTE_LIBRTE_TABLE=n
+CONFIG_RTE_TABLE_STATS_COLLECT=n
+# Compile librte_pipeline
+CONFIG_RTE_LIBRTE_PIPELINE=n
+CONFIG_RTE_PIPELINE_STATS_COLLECT=n
+# Compile librte_kni
+CONFIG_RTE_LIBRTE_KNI=n
+CONFIG_RTE_LIBRTE_PMD_KNI=n
+CONFIG_RTE_KNI_KMOD=n
+CONFIG_RTE_KNI_KMOD_ETHTOOL=n
+CONFIG_RTE_KNI_PREEMPT_DEFAULT=y
+# Compile architecture we compile for. pdump library
+CONFIG_RTE_LIBRTE_PDUMP=y
+# Compile vhost user library
+CONFIG_RTE_LIBRTE_VHOST=y
+CONFIG_RTE_LIBRTE_VHOST_NUMA=y
+CONFIG_RTE_LIBRTE_VHOST_DEBUG=n
+# Compile vhost PMD
+# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled.
+CONFIG_RTE_LIBRTE_PMD_VHOST=y
+# Compile IFC driver
+# To compile, CONFIG_RTE_LIBRTE_VHOST and CONFIG_RTE_EAL_VFIO
+# should be enabled.
+CONFIG_RTE_LIBRTE_IFC_PMD=n
+# Compile librte_bpf
+CONFIG_RTE_LIBRTE_BPF=n
+# allow load BPF from ELF files (requires libelf)
+CONFIG_RTE_LIBRTE_BPF_ELF=n
+# Compile architecture we compile for. test application
+CONFIG_RTE_APP_TEST=y
+CONFIG_RTE_APP_TEST_RESOURCE_TAR=n
+# Compile architecture we compile for. procinfo application
+CONFIG_RTE_PROC_INFO=y
+# Compile architecture we compile for. PMD test application
+CONFIG_RTE_TEST_PMD=n
+CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n
+CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n
+# Compile architecture we compile for. bbdev test application
+CONFIG_RTE_TEST_BBDEV=n
+# Compile architecture we compile for. crypto performance application
+CONFIG_RTE_APP_CRYPTO_PERF=n
+# Compile architecture we compile for. eventdev application
+CONFIG_RTE_APP_EVENTDEV=n
+CONFIG_RTE_EXEC_ENV_LINUXAPP=y
+CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n
+# Common libraries, before Bus/PMDs
+# NXP DPAA BUS and drivers
+# NXP FSLMC BUS and DPAA2 drivers
+# NXP ENETC PMD Driver
+CONFIG_RTE_ARCH_X86_64=y
+CONFIG_RTE_ARCH_X86=y
+CONFIG_RTE_ARCH_64=y
+CONFIG_RTE_TOOLCHAIN_GCC=y
+CONFIG_RTE_LIBRTE_PMD_XENVIRT=n
diff --git a/SPECS/ovn24.09.spec b/SPECS/ovn24.09.spec
new file mode 100644
index 0000000..a485f74
--- /dev/null
+++ b/SPECS/ovn24.09.spec
@@ -0,0 +1,686 @@
+# Copyright (C) 2009, 2010, 2013, 2014 Nicira Networks, Inc.
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without warranty of any kind.
+#
+# If tests have to be skipped while building, specify the '--without check'
+# option. For example:
+# rpmbuild -bb --without check rhel/openvswitch-fedora.spec
+
+# This defines the base package name's version.
+
+%define pkgver 2.13
+%define pkgname ovn24.09
+
+# If libcap-ng isn't available and there is no need for running OVS
+# as regular user, specify the '--without libcapng'
+%bcond_without libcapng
+
+# Enable PIE, bz#955181
+%global _hardened_build 1
+
+# RHEL-7 doesn't define _rundir macro yet
+# Fedora 15 onwards uses /run as _rundir
+%if 0%{!?_rundir:1}
+%define _rundir /run
+%endif
+
+# Build python2 (that provides python) and python3 subpackages on Fedora
+# Build only python3 (that provides python) subpackage on RHEL8
+# Build only python subpackage on RHEL7
+%if 0%{?rhel} > 7 || 0%{?fedora}
+# On RHEL8 Sphinx is included in buildroot
+%global external_sphinx 1
+%else
+# Don't use external sphinx (RHV doesn't have optional repositories enabled)
+%global external_sphinx 0
+%endif
+
+# We would see rpmlinit error - E: hardcoded-library-path in '% {_prefix}/lib'.
+# But there is no solution to fix this. Using {_lib} macro will solve the
+# rpmlink error, but will install the files in /usr/lib64/.
+# OVN pacemaker ocf script file is copied in /usr/lib/ocf/resource.d/ovn/
+# and we are not sure if pacemaker looks into this path to find the
+# OVN resource agent script.
+%global ovnlibdir %{_prefix}/lib
+
+Name: %{pkgname}
+Summary: Open Virtual Network support
+Group: System Environment/Daemons
+URL: http://www.ovn.org/
+Version: 24.09.0
+Release: 41%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
+Provides: openvswitch%{pkgver}-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-common < 2.11.0-1
+
+# Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
+# lib/sflow*.[ch] files are SISSL
+License: ASL 2.0 and LGPLv2+ and SISSL
+
+%define ovncommit 6c74ef66a877010a7cc43da61bc90c123d2b8b9d
+
+# Always pull an upstream release, since this is what we rebase to.
+Source: https://github.com/ovn-org/ovn/archive/%{ovncommit}.tar.gz#/ovn-%{version}.tar.gz
+
+%define ovscommit c598c05c85b2d38874a0ce8f7f088f6aae4fdabc
+%define ovsshortcommit c598c05
+
+Source10: https://github.com/openvswitch/ovs/archive/%{ovscommit}.tar.gz#/openvswitch-%{ovsshortcommit}.tar.gz
+%define ovsdir ovs-%{ovscommit}
+
+%define docutilsver 0.12
+%define pygmentsver 1.4
+%define sphinxver   1.1.3
+Source100: https://pypi.io/packages/source/d/docutils/docutils-%{docutilsver}.tar.gz
+Source101: https://pypi.io/packages/source/P/Pygments/Pygments-%{pygmentsver}.tar.gz
+Source102: https://pypi.io/packages/source/S/Sphinx/Sphinx-%{sphinxver}.tar.gz
+
+Source500: configlib.sh
+Source501: gen_config_group.sh
+Source502: set_config.sh
+
+# Important: source503 is used as the actual copy file
+# @TODO: this causes a warning - fix it?
+Source504: arm64-armv8a-linuxapp-gcc-config
+Source505: ppc_64-power8-linuxapp-gcc-config
+Source506: x86_64-native-linuxapp-gcc-config
+
+Patch:     %{pkgname}.patch
+
+# FIXME Sphinx is used to generate some manpages, unfortunately, on RHEL, it's
+# in the -optional repository and so we can't require it directly since RHV
+# doesn't have the -optional repository enabled and so TPS fails
+%if %{external_sphinx}
+BuildRequires: python3-sphinx
+%else
+# Sphinx dependencies
+BuildRequires: python-devel
+BuildRequires: python-setuptools
+#BuildRequires: python2-docutils
+BuildRequires: python-jinja2
+BuildRequires: python-nose
+#BuildRequires: python2-pygments
+# docutils dependencies
+BuildRequires: python-imaging
+# pygments dependencies
+BuildRequires: python-nose
+%endif
+
+BuildRequires: gcc gcc-c++ make
+BuildRequires: autoconf automake libtool
+BuildRequires: systemd-units openssl openssl-devel
+BuildRequires: python3-devel python3-setuptools
+BuildRequires: desktop-file-utils
+BuildRequires: groff-base graphviz
+BuildRequires: unbound-devel
+
+# make check dependencies
+BuildRequires: procps-ng
+%if 0%{?rhel} == 8 || 0%{?fedora}
+BuildRequires: python3-pyOpenSSL
+%endif
+BuildRequires: tcpdump
+
+%if %{with libcapng}
+BuildRequires: libcap-ng libcap-ng-devel
+%endif
+
+%if 0%{?rhel} >= 9
+BuildRequires: python3-scapy
+%endif
+
+Requires: hostname openssl iproute module-init-tools
+
+Requires(post): systemd-units
+Requires(preun): systemd-units
+Requires(postun): systemd-units
+
+# to skip running checks, pass --without check
+%bcond_without check
+
+%description
+OVN, the Open Virtual Network, is a system to support virtual network
+abstraction.  OVN complements the existing capabilities of OVS to add
+native support for virtual network abstractions, such as virtual L2 and L3
+overlays and security groups.
+
+%package central
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname}
+Requires: firewalld-filesystem
+Provides: openvswitch%{pkgver}-ovn-central = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-central < 2.11.0-1
+
+%description central
+OVN DB servers and ovn-northd running on a central node.
+
+%package host
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname}
+Requires: firewalld-filesystem
+Provides: openvswitch%{pkgver}-ovn-host = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-host < 2.11.0-1
+
+%description host
+OVN controller running on each host.
+
+%package vtep
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname}
+Provides: openvswitch%{pkgver}-ovn-vtep = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-vtep < 2.11.0-1
+
+%description vtep
+OVN vtep controller
+
+%prep
+%autosetup -n ovn-%{ovncommit} -a 10 -p 1
+
+%build
+%if 0%{?commit0:1}
+# fix the snapshot unreleased version to be the released one.
+sed -i.old -e "s/^AC_INIT(openvswitch,.*,/AC_INIT(openvswitch, %{version},/" configure.ac
+%endif
+./boot.sh
+
+# OVN source code is now separate.
+# Build openvswitch first.
+# XXX Current openvswitch2.13 doesn't
+# use "2.13.0" for version. It's a commit hash
+pushd %{ovsdir}
+./boot.sh
+%configure \
+%if %{with libcapng}
+        --enable-libcapng \
+%else
+        --disable-libcapng \
+%endif
+        --enable-ssl \
+        --with-pkidir=%{_sharedstatedir}/openvswitch/pki
+
+make %{?_smp_mflags}
+popd
+
+# Build OVN.
+# XXX OVS version needs to be updated when ovs2.13 is updated.
+%configure \
+        --with-ovs-source=$PWD/%{ovsdir} \
+%if %{with libcapng}
+        --enable-libcapng \
+%else
+        --disable-libcapng \
+%endif
+        --enable-ssl \
+        --with-pkidir=%{_sharedstatedir}/openvswitch/pki
+
+make %{?_smp_mflags}
+
+%install
+%make_install
+install -p -D -m 0644 \
+        rhel/usr_share_ovn_scripts_systemd_sysconfig.template \
+        $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/ovn
+
+for service in ovn-controller ovn-controller-vtep ovn-northd; do
+        install -p -D -m 0644 \
+                        rhel/usr_lib_systemd_system_${service}.service \
+                        $RPM_BUILD_ROOT%{_unitdir}/${service}.service
+done
+
+install -d -m 0755 $RPM_BUILD_ROOT/%{_sharedstatedir}/ovn
+
+install -d $RPM_BUILD_ROOT%{ovnlibdir}/firewalld/services/
+install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \
+        $RPM_BUILD_ROOT%{ovnlibdir}/firewalld/services/ovn-central-firewall-service.xml
+install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \
+        $RPM_BUILD_ROOT%{ovnlibdir}/firewalld/services/ovn-host-firewall-service.xml
+
+install -d -m 0755 $RPM_BUILD_ROOT%{ovnlibdir}/ocf/resource.d/ovn
+ln -s %{_datadir}/ovn/scripts/ovndb-servers.ocf \
+      $RPM_BUILD_ROOT%{ovnlibdir}/ocf/resource.d/ovn/ovndb-servers
+
+install -p -D -m 0644 rhel/etc_logrotate.d_ovn \
+        $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/ovn
+
+# remove unneeded files.
+rm -f $RPM_BUILD_ROOT%{_bindir}/ovs*
+rm -f $RPM_BUILD_ROOT%{_bindir}/vtep-ctl
+rm -f $RPM_BUILD_ROOT%{_sbindir}/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man5/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man5/vtep*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man7/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man8/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man8/vtep*
+rm -rf $RPM_BUILD_ROOT%{_datadir}/ovn/python
+rm -f $RPM_BUILD_ROOT%{_datadir}/ovn/scripts/ovs*
+rm -rf $RPM_BUILD_ROOT%{_datadir}/ovn/bugtool-plugins
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.a
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
+rm -f $RPM_BUILD_ROOT%{_libdir}/pkgconfig/*.pc
+rm -f $RPM_BUILD_ROOT%{_includedir}/ovn/*
+rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-appctl-bashcomp.bash
+rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-vsctl-bashcomp.bash
+rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/openvswitch
+rm -f $RPM_BUILD_ROOT%{_datadir}/ovn/scripts/ovn-bugtool*
+rm -f $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-overlay-driver \
+        $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-underlay-driver
+
+%check
+%if %{with check}
+    touch resolv.conf
+    export OVS_RESOLV_CONF=$(pwd)/resolv.conf
+    if ! make check TESTSUITEFLAGS='%{_smp_mflags}'; then
+        cat tests/testsuite.log
+        if ! make check TESTSUITEFLAGS='--recheck'; then
+            cat tests/testsuite.log
+            # Presently a test case - "2796: ovn -- ovn-controller incremental processing"
+            # is failing on aarch64 arch. Let's not exit for this arch
+            # until we figure out why it is failing.
+            # Test case 93: ovn.at:12105       ovn -- ACLs on Port Groups is failing
+            # repeatedly on s390x. This needs to be investigated.
+            %ifnarch aarch64
+            %ifnarch ppc64le
+            %ifnarch s390x
+                exit 1
+            %endif
+            %endif
+            %endif
+        fi
+    fi
+%endif
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%pre central
+if [ $1 -eq 1 ] ; then
+    # Package install.
+    /bin/systemctl status ovn-northd.service >/dev/null
+    ovn_status=$?
+    rpm -ql openvswitch-ovn-central > /dev/null
+    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
+        # ovn-northd service is running which means old openvswitch-ovn-central
+        # is already installed and it will be cleaned up. So start ovn-northd
+        # service when posttrans central is called.
+        touch %{_localstatedir}/lib/rpm-state/ovn-northd
+    fi
+fi
+
+%pre host
+if [ $1 -eq 1 ] ; then
+    # Package install.
+    /bin/systemctl status ovn-controller.service >/dev/null
+    ovn_status=$?
+    rpm -ql openvswitch-ovn-host > /dev/null
+    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
+        # ovn-controller service is running which means old
+        # openvswitch-ovn-host is installed and it will be cleaned up. So
+        # start ovn-controller service when posttrans host is called.
+        touch %{_localstatedir}/lib/rpm-state/ovn-controller
+    fi
+fi
+
+%pre vtep
+if [ $1 -eq 1 ] ; then
+    # Package install.
+    /bin/systemctl status ovn-controller-vtep.service >/dev/null
+    ovn_status=$?
+    rpm -ql openvswitch-ovn-vtep > /dev/null
+    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
+        # ovn-controller-vtep service is running which means old
+        # openvswitch-ovn-vtep is installed and it will be cleaned up. So
+        # start ovn-controller-vtep service when posttrans host is called.
+        touch %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
+    fi
+fi
+
+%preun central
+%if 0%{?systemd_preun:1}
+    %systemd_preun ovn-northd.service
+%else
+    if [ $1 -eq 0 ] ; then
+        # Package removal, not upgrade
+        /bin/systemctl --no-reload disable ovn-northd.service >/dev/null 2>&1 || :
+        /bin/systemctl stop ovn-northd.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%preun host
+%if 0%{?systemd_preun:1}
+    %systemd_preun ovn-controller.service
+%else
+    if [ $1 -eq 0 ] ; then
+        # Package removal, not upgrade
+        /bin/systemctl --no-reload disable ovn-controller.service >/dev/null 2>&1 || :
+        /bin/systemctl stop ovn-controller.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%preun vtep
+%if 0%{?systemd_preun:1}
+    %systemd_preun ovn-controller-vtep.service
+%else
+    if [ $1 -eq 0 ] ; then
+        # Package removal, not upgrade
+        /bin/systemctl --no-reload disable ovn-controller-vtep.service >/dev/null 2>&1 || :
+        /bin/systemctl stop ovn-controller-vtep.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%post
+%if %{with libcapng}
+if [ $1 -eq 1 ]; then
+    sed -i 's:^#OVN_USER_ID=:OVN_USER_ID=:' %{_sysconfdir}/sysconfig/ovn
+    sed -i 's:\(.*su\).*:\1 openvswitch openvswitch:' %{_sysconfdir}/logrotate.d/ovn
+fi
+%endif
+
+%post central
+%if 0%{?systemd_post:1}
+    %systemd_post ovn-northd.service
+%else
+    # Package install, not upgrade
+    if [ $1 -eq 1 ]; then
+        /bin/systemctl daemon-reload >dev/null || :
+    fi
+%endif
+
+%post host
+%if 0%{?systemd_post:1}
+    %systemd_post ovn-controller.service
+%else
+    # Package install, not upgrade
+    if [ $1 -eq 1 ]; then
+        /bin/systemctl daemon-reload >dev/null || :
+    fi
+%endif
+
+%post vtep
+%if 0%{?systemd_post:1}
+    %systemd_post ovn-controller-vtep.service
+%else
+    # Package install, not upgrade
+    if [ $1 -eq 1 ]; then
+        /bin/systemctl daemon-reload >dev/null || :
+    fi
+%endif
+
+%postun
+
+%postun central
+%if 0%{?systemd_postun_with_restart:1}
+    %systemd_postun_with_restart ovn-northd.service
+%else
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+    if [ "$1" -ge "1" ] ; then
+    # Package upgrade, not uninstall
+        /bin/systemctl try-restart ovn-northd.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%postun host
+%if 0%{?systemd_postun_with_restart:1}
+    %systemd_postun_with_restart ovn-controller.service
+%else
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+    if [ "$1" -ge "1" ] ; then
+        # Package upgrade, not uninstall
+        /bin/systemctl try-restart ovn-controller.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%postun vtep
+%if 0%{?systemd_postun_with_restart:1}
+    %systemd_postun_with_restart ovn-controller-vtep.service
+%else
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+    if [ "$1" -ge "1" ] ; then
+        # Package upgrade, not uninstall
+        /bin/systemctl try-restart ovn-controller-vtep.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%posttrans central
+if [ $1 -eq 1 ]; then
+    # Package install, not upgrade
+    if [ -e %{_localstatedir}/lib/rpm-state/ovn-northd ]; then
+        rm %{_localstatedir}/lib/rpm-state/ovn-northd
+        /bin/systemctl start ovn-northd.service >/dev/null 2>&1 || :
+    fi
+fi
+
+
+%posttrans host
+if [ $1 -eq 1 ]; then
+    # Package install, not upgrade
+    if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller ]; then
+        rm %{_localstatedir}/lib/rpm-state/ovn-controller
+        /bin/systemctl start ovn-controller.service >/dev/null 2>&1 || :
+    fi
+fi
+
+%posttrans vtep
+if [ $1 -eq 1 ]; then
+    # Package install, not upgrade
+    if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller-vtep ]; then
+        rm %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
+        /bin/systemctl start ovn-controller-vtep.service >/dev/null 2>&1 || :
+    fi
+fi
+
+%files
+%{_bindir}/ovn-nbctl
+%{_bindir}/ovn-sbctl
+%{_bindir}/ovn-trace
+%{_bindir}/ovn-detrace
+%{_bindir}/ovn_detrace.py
+%{_bindir}/ovn-appctl
+%{_bindir}/ovn-ic-nbctl
+%{_bindir}/ovn-ic-sbctl
+%{_bindir}/ovn-debug
+%dir %{_datadir}/ovn/
+%dir %{_datadir}/ovn/scripts/
+%{_datadir}/ovn/scripts/ovn-ctl
+%{_datadir}/ovn/scripts/ovn-lib
+%{_datadir}/ovn/scripts/ovndb-servers.ocf
+%{_mandir}/man8/ovn-ctl.8*
+%{_mandir}/man8/ovn-appctl.8*
+%{_mandir}/man8/ovn-nbctl.8*
+%{_mandir}/man8/ovn-ic-nbctl.8*
+%{_mandir}/man8/ovn-trace.8*
+%{_mandir}/man1/ovn-detrace.1*
+%{_mandir}/man7/ovn-architecture.7*
+%{_mandir}/man8/ovn-sbctl.8*
+%{_mandir}/man8/ovn-ic-sbctl.8*
+%{_mandir}/man8/ovn-debug.8*
+%{_mandir}/man5/ovn-nb.5*
+%{_mandir}/man5/ovn-ic-nb.5*
+%{_mandir}/man5/ovn-sb.5*
+%{_mandir}/man5/ovn-ic-sb.5*
+%dir %{ovnlibdir}/ocf/resource.d/ovn/
+%{ovnlibdir}/ocf/resource.d/ovn/ovndb-servers
+%config(noreplace) %verify(not md5 size mtime) %{_sysconfdir}/logrotate.d/ovn
+%config(noreplace) %verify(not md5 size mtime) %{_sysconfdir}/sysconfig/ovn
+
+%files central
+%{_bindir}/ovn-northd
+%{_bindir}/ovn-ic
+%{_mandir}/man8/ovn-northd.8*
+%{_mandir}/man8/ovn-ic.8*
+%{_datadir}/ovn/ovn-nb.ovsschema
+%{_datadir}/ovn/ovn-ic-nb.ovsschema
+%{_datadir}/ovn/ovn-sb.ovsschema
+%{_datadir}/ovn/ovn-ic-sb.ovsschema
+%{_unitdir}/ovn-northd.service
+%{ovnlibdir}/firewalld/services/ovn-central-firewall-service.xml
+
+%files host
+%{_bindir}/ovn-controller
+%{_mandir}/man8/ovn-controller.8*
+%{_unitdir}/ovn-controller.service
+%{ovnlibdir}/firewalld/services/ovn-host-firewall-service.xml
+
+%files vtep
+%{_bindir}/ovn-controller-vtep
+%{_mandir}/man8/ovn-controller-vtep.8*
+%{_unitdir}/ovn-controller-vtep.service
+
+%changelog
+* Fri Oct 11 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-41
+- ovn-ic: Fix potential segmentation violation.
+[Upstream: 0611308a7a2e58911a18d974b1e8c7b2d8f0541f]
+
+* Wed Oct 09 2024 Mohammad Heib <mheib@redhat.com> - 24.09.0-40
+- controller: Container lport install flows in MAIN chassis only. (#FDP-772)
+[Upstream: 809d4aac64c4343f3bf845f2df497be2af6cba26]
+
+* Fri Oct 04 2024 Ales Musil <amusil@redhat.com> - 24.09.0-39
+- northd, controller: Use ct_next to get the CT state for direct SNAT. (#FDP-744)
+[Upstream: 2a60ba94f99486f1eb44134e693a8393059d4869]
+
+* Tue Oct 01 2024 Ales Musil <amusil@redhat.com> - 24.09.0-38
+- ci: Pin scapy version.
+[Upstream: 4579cb66599fedb433f8fc2fa0d9167e54feb457]
+
+* Thu Sep 26 2024 Rosemarie O'Riorden <rosemarie@redhat.com> - 24.09.0-37
+- northd: Respect --ecmp-symmetric-reply for single routes. (#FDP-786)
+[Upstream: 7b00627433f3ee066cf6a5b727fec614c5e6eb77]
+
+* Fri Sep 20 2024 Ales Musil <amusil@redhat.com> - 24.09.0-36
+- controller: Avoid quadratic complexity for multi-chassis ports.
+[Upstream: b3e47dc70ad6bd2f92ffbe64a39ae4d98c0e8398]
+
+* Fri Sep 20 2024 Ales Musil <amusil@redhat.com> - 24.09.0-35
+- physical: Prevent wrong FDB to be learned with multichassis port. (#FDP-620)
+[Upstream: 8b3e276e228407068e0a9e72ad3e02969da2de1d]
+
+* Thu Sep 19 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-34
+- ovn-controller: Fix potential assert when exiting.
+[Upstream: 8488c3c0aff73d1037a4af205ac3ba2ed22106a3]
+
+* Fri Sep 13 2024 Mark Michelson <mmichels@redhat.com> - 24.09.0-33
+- Prepare for 24.09.1.
+[Upstream: 2282ae8501faa5fe9bd607a5f0a81d863441f10f]
+
+* Fri Sep 13 2024 Mark Michelson <mmichels@redhat.com> - 24.09.0-32
+- Set release date for 24.09.0.
+[Upstream: f04e445562597909038f663a5da0699183002bcf]
+
+* Wed Sep 11 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-31
+- tests: Fix flaky BFD system test.
+[Upstream: 7be7d8df2704eae6cc26111bc347bd0d5601fb09]
+
+* Wed Sep 11 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-30
+- tests: Fix flaky ACL Sampling system tests.
+[Upstream: 88e80a3b8ef21fef7418b03c49735dfd6af11931]
+
+* Wed Sep 11 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-29
+- tests: Fix multiple ovn-ic race conditions.
+[Upstream: 2e6cdfdf18da671d462bc2b575cd48fa20a7e903]
+
+* Wed Sep 11 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-28
+- tests: Fix flaky "load-balancer template IPv4".
+[Upstream: 1a69a5d4c7fc37c3aee8e4fa07fe50aada59b997]
+
+* Wed Sep 11 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-27
+- tests: Fix flaky "Sampling_App incremental processing".
+[Upstream: ecdc7bb7b46e16321679b9a3f064897f7cae90cd]
+
+* Wed Sep 11 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-26
+- tests: Fix flaky "MAC binding aging".
+[Upstream: afed92ce6839473fd56278aa1879ad5e6b5c086e]
+
+* Wed Sep 11 2024 Ilya Maximets <i.maximets@ovn.org> - 24.09.0-25
+- ovs: Move the submodule to the official v3.4.0 release.
+[Upstream: 2f14de89f05d1fce901d374e3f2dd7e9d6e50365]
+
+* Wed Sep 11 2024 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 24.09.0-24
+- controller: Do not remove snat-ct-zone requested by the CMS. (#FDP-773)
+[Upstream: 76bd54eb36d52d1b3fa663d185f53dcaa2cff06b]
+
+* Tue Sep 03 2024 Vladislav Odintsov <odivlad@gmail.com> - 24.09.0-23
+- news: Fix indentation for an entry.
+[Upstream: 360f669b16ec2ed64657ea1d4c75b73263812bad]
+
+* Thu Aug 29 2024 Ales Musil <amusil@redhat.com> - 24.09.0-22
+- pinctrl: Explicitly set ICMPv6 code for packet too big. (#FDP-763)
+[Upstream: 8efac26f6637fc35fd1d3e5b41b935ebcb074a1d]
+
+* Wed Aug 28 2024 Ales Musil <amusil@redhat.com> - 24.09.0-21
+- tests: Prevent netcat from forking.
+[Upstream: dc5b666d7dba02c0a5617605463967c86739568e]
+
+* Tue Aug 27 2024 Mark Michelson <mmichels@redhat.com> - 24.09.0-20
+- Documentation: Add inclusive-language documentation.
+[Upstream: ea24d593e921c1e35fe281b9c1de449ab7b5c56f]
+
+* Tue Aug 27 2024 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 24.09.0-19
+- Revert "northd: Introduce ECMP_Nexthop table in SB db.". (#FDP-750)
+[Upstream: cb8e5433281b8ab5f3a65e52e41f46839e32d4fc]
+
+* Tue Aug 27 2024 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 24.09.0-18
+- Revert "northd: Add nexhop id in ct_label.label.". (#FDP-750)
+[Upstream: 7b937370bb4befb0d69f310f14d6068589d6740c]
+
+* Tue Aug 27 2024 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 24.09.0-17
+- Revert "ofctrl: Introduce ecmp_nexthop_monitor.". (#FDP-750)
+[Upstream: 42148ffe8c6aa95f732f4c2444b4d10dbbf2fd93]
+
+* Thu Aug 22 2024 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 24.09.0-16
+- northd: Make bfd_connections static in build_parsed_routes.
+[Upstream: fc8a015434f18b6b8ef05b11179f89d099fee2ce]
+
+* Thu Aug 22 2024 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 24.09.0-15
+- northd: Get rid of bfd_connections in bfd_sync_data.
+[Upstream: f29fc462e3e5313d6e7bb234fb7864c587e11c32]
+
+* Thu Aug 22 2024 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 24.09.0-14
+- northd: Optimize lookup in bfd_is_port_running.
+[Upstream: 8b08b23dc14130e6e5425f132a5be8a2866f8b54]
+
+* Thu Aug 22 2024 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 24.09.0-13
+- northd: Introduce bfd_sync_data data structure.
+[Upstream: 8f085f0bd1c0f70b0260729d6b4ad4223b920d54]
+
+* Tue Aug 20 2024 Numan Siddique <numans@ovn.org> - 24.09.0-12
+- northd: Fix potential crash when creating chassisredirect port.
+[Upstream: 97fc54f10f2c55a30df01d8f2c4f772d0960c114]
+
+* Mon Aug 19 2024 Numan Siddique <numans@ovn.org> - 24.09.0-11
+- tests: Skip "IPv6 switching - megaflow check" if scapy is not installed.
+[Upstream: 34fa3c1d4db31e2b840e6592b418f53dc6efcf5a]
+
+* Fri Aug 16 2024 Numan Siddique <numans@ovn.org> - 24.09.0-10
+- Reply only for the multicast ND solicitations. (#FDP-728)
+[Upstream: 30a502e9f3a8f66370acdc9b4c1c2ae96af5451f]
+
+* Wed Aug 14 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-9
+- multinode: Fix test "ovn multinode NAT ...".
+[Upstream: 0fbe412a1e96790f16c9015f89473c30730966ce]
+
+* Wed Aug 14 2024 Xavier Simonart <xsimonar@redhat.com> - 24.09.0-8
+- multinode: Increase maximum execution time.
+[Upstream: 155637d986730dd5af18b936f3ac736931d164fe]
+
+* Tue Aug 13 2024 Martin Kalcok <martin.kalcok@canonical.com> - 24.09.0-7
+- northd: Routing protocol port redirection.
+[Upstream: 158597a1fa4ef253e3c461da9eaf26b638d68b69]
+
+* Mon Aug 12 2024 Rosemarie O'Riorden <roriorden@redhat.com> - 24.09.0-6
+- northd: Clean up SB MAC bindings for deleted ports. (#FDP-723)
+[Upstream: 11d92550b15c4aa03cc28386d89fb09df47aa2b5]
+
+* Mon Aug 12 2024 Ales Musil <amusil@redhat.com> - 24.09.0-5
+- controller: Make sure the meter and group tables are initialized.
+[Upstream: 4ded211f808185da5ff16a67d6daf1759204cbab]
+
+* Mon Aug 12 2024 Ilya Maximets <i.maximets@ovn.org> - 24.09.0-4
+- github: containers: Fix job condition.
+[Upstream: 07d51ea3cbf17f1fa354d5982d4026ac15c94d40]
+