diff --git a/.ovn.metadata b/.ovn.metadata
index a8083bb..906eb34 100644
--- a/.ovn.metadata
+++ b/.ovn.metadata
@@ -1,5 +1,5 @@
 002450621b33c5690060345b0aac25bc2426d675  SOURCES/docutils-0.12.tar.gz
-19829758c492a62983f4536c63e4654e9af934c2  SOURCES/openvswitch-ec1d730.tar.gz
+8746fd38571d535d663102d7023d40ae5f9f2329  SOURCES/openvswitch-49e64f1.tar.gz
 1ad2a2bdb68e5e984310a8519d190350001f433a  SOURCES/ovn-23.09.0.tar.gz
 d34f96421a86004aa5d26ecf975edefd09f948b1  SOURCES/Pygments-1.4.tar.gz
 6beb30f18ffac3de7689b7fd63e9a8a7d9c8df3a  SOURCES/Sphinx-1.1.3.tar.gz
diff --git a/SOURCES/ovn23.09.patch b/SOURCES/ovn23.09.patch
index 9802b63..f439450 100644
--- a/SOURCES/ovn23.09.patch
+++ b/SOURCES/ovn23.09.patch
@@ -88,14 +88,14 @@ index 12f819017..eda8b6d02 100644
  COPY --from=ovnkubebuilder /root/ovn-kubernetes/go-controller/_output/go/bin/ovndbchecker /usr/bin/
  COPY --from=ovnkubebuilder /root/ovn-kubernetes/go-controller/_output/go/bin/ovn-k8s-cni-overlay /usr/libexec/cni/ovn-k8s-cni-overlay
 diff --git a/.cirrus.yml b/.cirrus.yml
-index bd4cd08aa..dd2aa1bfd 100644
+index bd4cd08aa..3b53c45ae 100644
 --- a/.cirrus.yml
 +++ b/.cirrus.yml
 @@ -1,14 +1,33 @@
 -arm_unit_tests_task:
 +compute_engine_instance:
 +  image_project: ubuntu-os-cloud
-+  image: family/ubuntu-2304-arm64
++  image: family/ubuntu-2310-arm64
 +  architecture: arm64
 +  platform: linux
 +  memory: 4G
@@ -111,14 +111,14 @@ index bd4cd08aa..dd2aa1bfd 100644
 +    - cd utilities/containers
 +    - make ubuntu
 +    - podman save -o /tmp/image.tar ovn-org/ovn-tests:ubuntu
-+
-+  upload_image_script:
-+    - curl -s -X POST -T /tmp/image.tar http://$CIRRUS_HTTP_CACHE_HOST/${CIRRUS_CHANGE_IN_REPO}
  
 -  arm_container:
 -    image: ghcr.io/ovn-org/ovn-tests:fedora
 -    memory: 4G
 -    cpu: 2
++  upload_image_script:
++    - curl -s -X POST -T /tmp/image.tar http://$CIRRUS_HTTP_CACHE_HOST/${CIRRUS_CHANGE_IN_REPO}
++
 +arm_unit_tests_task:
 +  depends_on:
 +    - build_image
@@ -131,7 +131,7 @@ index bd4cd08aa..dd2aa1bfd 100644
      matrix:
        - CC: gcc
          TESTSUITE: test
-@@ -31,5 +50,16 @@ arm_unit_tests_task:
+@@ -31,5 +50,22 @@ arm_unit_tests_task:
  
    name: ARM64 ${CC} ${TESTSUITE} ${TEST_RANGE}
  
@@ -139,6 +139,12 @@ index bd4cd08aa..dd2aa1bfd 100644
 +    - sudo apt update
 +    - sudo apt install -y podman
 +
++  #  XXX This should be removed when native crun >=1.9.1
++  update_crun_script:
++    - crun --version
++    - curl -L "https://github.com/containers/crun/releases/download/1.14.1/crun-1.14.1-linux-arm64" -o /usr/bin/crun
++    - chmod +x /usr/bin/crun
++
 +  download_cache_script:
 +    - curl http://$CIRRUS_HTTP_CACHE_HOST/${CIRRUS_CHANGE_IN_REPO} -o /tmp/image.tar
 +
@@ -150,10 +156,10 @@ index bd4cd08aa..dd2aa1bfd 100644
 -    - ./.ci/linux-build.sh
 +    - ./.ci/ci.sh --archive-logs
 diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml
-index 57e815ed8..bdd118087 100644
+index 57e815ed8..87e28d645 100644
 --- a/.github/workflows/containers.yml
 +++ b/.github/workflows/containers.yml
-@@ -15,7 +15,7 @@ env:
+@@ -15,12 +15,12 @@ env:
  
  jobs:
    container:
@@ -162,8 +168,14 @@ index 57e815ed8..bdd118087 100644
      strategy:
        matrix:
          distro: [ fedora, ubuntu ]
+     steps:
+-      - uses: actions/checkout@v3
++      - uses: actions/checkout@v4
+ 
+       - name: Update APT cache
+         run: sudo apt update
 diff --git a/.github/workflows/ovn-fake-multinode-tests.yml b/.github/workflows/ovn-fake-multinode-tests.yml
-index 75c5ca818..25610df53 100644
+index 75c5ca818..79b6c4253 100644
 --- a/.github/workflows/ovn-fake-multinode-tests.yml
 +++ b/.github/workflows/ovn-fake-multinode-tests.yml
 @@ -13,7 +13,7 @@ concurrency:
@@ -175,7 +187,40 @@ index 75c5ca818..25610df53 100644
      strategy:
        matrix:
          cfg:
-@@ -69,7 +69,7 @@ jobs:
+@@ -26,7 +26,7 @@ jobs:
+       XDG_RUNTIME_DIR: ''
+     steps:
+     - name: Check out ovn-fake-multi-node
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+         repository: 'ovn-org/ovn-fake-multinode'
+         path: 'ovn-fake-multinode'
+@@ -36,14 +36,14 @@ jobs:
+     # ovn-fake-multinode builds and installs ovs from ovn-fake-multinode/ovs
+     # and it builds and installs ovn from ovn-fake-multinode/ovn. It uses the ovs submodule for ovn compilation.
+     - name: Check out ovs master
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+         path: 'ovn-fake-multinode/ovs'
+         repository: 'openvswitch/ovs'
+         ref: 'master'
+ 
+     - name: Check out ovn ${{ matrix.cfg.branch }}
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+         path: 'ovn-fake-multinode/ovn'
+         submodules: recursive
+@@ -63,21 +63,21 @@ jobs:
+         sudo podman save ovn/ovn-multi-node:${{ matrix.cfg.branch }} > /tmp/_output/ovn_${{ matrix.cfg.branch }}_image.tar
+       working-directory: ovn-fake-multinode
+ 
+-    - uses: actions/upload-artifact@v3
++    - uses: actions/upload-artifact@v4
+       with:
+         name: test-${{ matrix.cfg.branch }}-image
          path: /tmp/_output/ovn_${{ matrix.cfg.branch }}_image.tar
  
    multinode-tests:
@@ -184,12 +229,22 @@ index 75c5ca818..25610df53 100644
      timeout-minutes: 15
      needs: [build]
      strategy:
-@@ -99,13 +99,18 @@ jobs:
+       fail-fast: false
+       matrix:
+         cfg:
+-        - { branch: "main" }
+-        - { branch: "branch-22.03" }
++        - { branch: "main", testsuiteflags: ""}
++        - { branch: "branch-22.03", testsuiteflags: "-k 'basic test'" }
+     name: multinode tests ${{ join(matrix.cfg.*, ' ') }}
+     env:
+       RUNC_CMD: podman
+@@ -99,19 +99,24 @@ jobs:
        XDG_RUNTIME_DIR: ''
  
      steps:
 +    - name: Check out ovn
-+      uses: actions/checkout@v3
++      uses: actions/checkout@v4
 +
      - name: install required dependencies
        run:  |
@@ -202,19 +257,88 @@ index 75c5ca818..25610df53 100644
 +        . .ci/linux-util.sh
 +        free_up_disk_space_ubuntu
  
-     - uses: actions/download-artifact@v3
+-    - uses: actions/download-artifact@v3
++    - uses: actions/download-artifact@v4
+       with:
+         name: test-main-image
+ 
+-    - uses: actions/download-artifact@v3
++    - uses: actions/download-artifact@v4
+       with:
+         name: test-branch-22.03-image
+ 
+@@ -121,7 +126,7 @@ jobs:
+         sudo podman load --input ovn_branch-22.03_image.tar
+ 
+     - name: Check out ovn-fake-multi-node
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
        with:
-@@ -153,7 +158,7 @@ jobs:
+         repository: 'ovn-org/ovn-fake-multinode'
+         path: 'ovn-fake-multinode'
+@@ -132,6 +137,14 @@ jobs:
+         sudo systemctl start openvswitch-switch
+         sudo ovs-vsctl show
+ 
++    # XXX This should be removed when native crun >=1.9.1
++    - name: update crun script
++      run: |
++        crun --version
++        sudo curl -L "https://github.com/containers/crun/releases/download/1.14.1/crun-1.14.1-linux-amd64" -o /usr/bin/crun
++        sudo chmod +x /usr/bin/crun
++        echo "New crun version: "$(crun --version)
++
+     - name: Start basic cluster
+       run: |
+         sudo -E ./ovn_cluster.sh start
+@@ -151,12 +164,12 @@ jobs:
+         echo "$HOME/.local/bin" >> $GITHUB_PATH
+ 
      - name: set up python
-       uses: actions/setup-python@v4
+-      uses: actions/setup-python@v4
++      uses: actions/setup-python@v5
        with:
 -        python-version: '3.x'
 +        python-version: '3.12'
  
      - name: Check out ovn
-       uses: actions/checkout@v3
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+         path: 'ovn'
+         submodules: recursive
+@@ -171,7 +184,7 @@ jobs:
+ 
+     - name: Run fake-multinode system tests
+       run: |
+-        if ! sudo make check-multinode; then
++        if ! sudo make check-multinode TESTSUITEFLAGS="${{ matrix.cfg.testsuiteflags }}"; then
+           sudo podman exec -it ovn-central ovn-nbctl show || :
+           sudo podman exec -it ovn-central ovn-sbctl show || :
+           sudo podman exec -it ovn-chassis-1 ovs-vsctl show || :
+@@ -185,9 +198,9 @@ jobs:
+     - name: copy logs on failure
+       if: failure() || cancelled()
+       run: |
+-        # upload-artifact@v3 throws exceptions if it tries to upload socket
++        # upload-artifact throws exceptions if it tries to upload socket
+         # files and we could have some socket files in testsuite.dir.
+-        # Also, upload-artifact@v3 doesn't work well enough with wildcards.
++        # Also, upload-artifact doesn't work well enough with wildcards.
+         # So, we're just archiving everything here to avoid any issues.
+         mkdir logs
+         cp ovn/config.log ./logs/
+@@ -198,7 +211,7 @@ jobs:
+ 
+     - name: upload logs on failure
+       if: failure() || cancelled()
+-      uses: actions/upload-artifact@v3
++      uses: actions/upload-artifact@v4
+       with:
+         name: logs-linux-${{ join(matrix.cfg.*, '-') }}
+         path: logs.tgz
 diff --git a/.github/workflows/ovn-kubernetes.yml b/.github/workflows/ovn-kubernetes.yml
-index 69ab0566d..1689396d6 100644
+index 69ab0566d..0f2b30497 100644
 --- a/.github/workflows/ovn-kubernetes.yml
 +++ b/.github/workflows/ovn-kubernetes.yml
 @@ -24,7 +24,7 @@ env:
@@ -226,6 +350,30 @@ index 69ab0566d..1689396d6 100644
      steps:
      - name: Enable Docker experimental features
        run: |
+@@ -32,12 +32,12 @@ jobs:
+         sudo service docker restart
+ 
+     - name: Check out ovn
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+         submodules: recursive
+ 
+     - name: Check out ovn-kubernetes
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+           path: src/github.com/ovn-org/ovn-kubernetes
+           repository: ovn-org/ovn-kubernetes
+@@ -54,7 +54,7 @@ jobs:
+         mkdir /tmp/_output
+         docker save ovn-daemonset-f:dev > /tmp/_output/image.tar
+ 
+-    - uses: actions/upload-artifact@v3
++    - uses: actions/upload-artifact@v4
+       with:
+         name: test-image
+         path: /tmp/_output/image.tar
 @@ -62,7 +62,7 @@ jobs:
    e2e:
      name: e2e
@@ -235,12 +383,15 @@ index 69ab0566d..1689396d6 100644
      timeout-minutes: 220
      strategy:
        fail-fast: false
-@@ -95,18 +95,14 @@ jobs:
+@@ -95,20 +95,16 @@ jobs:
        KIND_IPV6_SUPPORT: "${{ matrix.ipfamily == 'IPv6' || matrix.ipfamily == 'dualstack' }}"
      steps:
  
--    - name: Free up disk space
--      run: |
++    - name: Check out ovn
++      uses: actions/checkout@v4
++
+     - name: Free up disk space
+       run: |
 -        sudo eatmydata apt-get purge --auto-remove -y \
 -          azure-cli aspnetcore-* dotnet-* ghc-* firefox \
 -          google-chrome-stable google-cloud-sdk \
@@ -248,21 +399,86 @@ index 69ab0566d..1689396d6 100644
 -          msbuild mysql-server-core-* php-* php7* \
 -          powershell temurin-* zulu-*
 -
-     - name: Check out ovn
-       uses: actions/checkout@v3
- 
-+    - name: Free up disk space
-+      run: |
+-    - name: Check out ovn
+-      uses: actions/checkout@v3
 +        . .ci/linux-util.sh
 +        free_up_disk_space_ubuntu
-+
+ 
      - name: Check out ovn-kubernetes
-       uses: actions/checkout@v3
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+           path: src/github.com/ovn-org/ovn-kubernetes
+           repository: ovn-org/ovn-kubernetes
+@@ -118,9 +114,10 @@ jobs:
+         .ci/ovn-kubernetes/prepare.sh src/github.com/ovn-org/ovn-kubernetes $GITHUB_ENV
+ 
+     - name: Set up Go
+-      uses: actions/setup-go@v3
++      uses: actions/setup-go@v5
+       with:
+         go-version: ${{ env.GO_VERSION }}
++        cache-dependency-path: "**/*.sum"
+       id: go
+ 
+     - name: Set up GOPATH
+@@ -135,7 +132,7 @@ jobs:
+       run: |
+         sudo ufw disable
+ 
+-    - uses: actions/download-artifact@v3
++    - uses: actions/download-artifact@v4
+       with:
+         name: test-image
+ 
+@@ -159,7 +156,7 @@ jobs:
+ 
+     - name: Upload Junit Reports
+       if: always()
+-      uses: actions/upload-artifact@v3
++      uses: actions/upload-artifact@v4
        with:
+         name: kind-junit-${{ env.JOB_NAME }}-${{ github.run_id }}
+         path: 'src/github.com/ovn-org/ovn-kubernetes/test/_artifacts/*.xml'
+@@ -173,7 +170,7 @@ jobs:
+ 
+     - name: Upload logs
+       if: always()
+-      uses: actions/upload-artifact@v3
++      uses: actions/upload-artifact@v4
+       with:
+         name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }}
+         path: /tmp/kind/logs
 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
-index fe2a14c40..aa01dc7e9 100644
+index fe2a14c40..85916548a 100644
 --- a/.github/workflows/test.yml
 +++ b/.github/workflows/test.yml
+@@ -26,7 +26,7 @@ jobs:
+ 
+     steps:
+     - name: checkout
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+ 
+     - name: update PATH
+       run: |
+@@ -54,14 +54,14 @@ jobs:
+ 
+     - name: cache
+       id: dpdk_cache
+-      uses: actions/cache@v3
++      uses: actions/cache@v4
+       with:
+         path: dpdk-dir
+         key: ${{ steps.gen_dpdk_key.outputs.key }}
+ 
+     - name: set up python
+       if: steps.dpdk_cache.outputs.cache-hit != 'true'
+-      uses: actions/setup-python@v4
++      uses: actions/setup-python@v5
+       with:
+         python-version: '3.9'
+ 
 @@ -80,10 +80,62 @@ jobs:
        if: steps.dpdk_cache.outputs.cache-hit != 'true'
        run: ./.ci/dpdk-build.sh
@@ -283,7 +499,7 @@ index fe2a14c40..aa01dc7e9 100644
 +    runs-on: ubuntu-22.04
 +
 +    steps:
-+      - uses: actions/checkout@v3
++      - uses: actions/checkout@v4
 +
 +      - name: Update APT cache
 +        run: sudo apt update
@@ -315,7 +531,7 @@ index fe2a14c40..aa01dc7e9 100644
 +
 +      - name: Cache image
 +        id: image_cache
-+        uses: actions/cache@v3
++        uses: actions/cache@v4
 +        with:
 +          path: /tmp/image.tar
 +          key: ${{ github.sha }}
@@ -337,24 +553,57 @@ index fe2a14c40..aa01dc7e9 100644
  
      strategy:
        fail-fast: false
-@@ -157,13 +209,25 @@ jobs:
+@@ -130,20 +182,20 @@ jobs:
+     steps:
+     - name: checkout
+       if: github.event_name == 'push' || github.event_name == 'pull_request'
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+         submodules: recursive
+ 
+     # For weekly runs, don't update submodules
+     - name: checkout without submodule
+       if: github.event_name == 'schedule'
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+ 
+     # Weekly runs test using the tip of the most recent stable OVS branch
+     # instead of the submodule.
+     - name: checkout OVS
+       if: github.event_name == 'schedule'
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+         repository: 'openvswitch/ovs'
+         fetch-depth: 0
+@@ -157,13 +209,33 @@ jobs:
              sort -V | tail -1)
        working-directory: ovs
  
 -    - name: cache
 +    - name: cache dpdk
        if: matrix.cfg.dpdk != ''
-       uses: actions/cache@v3
+-      uses: actions/cache@v3
++      uses: actions/cache@v4
        with:
          path: dpdk-dir
          key: ${{ needs.build-dpdk.outputs.dpdk_key }}
  
 +    - name: image cache
-+      uses: actions/cache@v3
++      uses: actions/cache@v4
 +      with:
 +        path: /tmp/image.tar
 +        key: ${{ github.sha }}
 +
++    # XXX This should be removed when native crun >=1.9.1
++    - name: update crun script
++      run: |
++        crun --version
++        sudo curl -L "https://github.com/containers/crun/releases/download/1.14.1/crun-1.14.1-linux-amd64" -o /usr/bin/crun
++        sudo chmod +x /usr/bin/crun
++        echo "New crun version: "$(crun --version)
++
 +    - name: load image
 +      run: |
 +        sudo podman load -i /tmp/image.tar
@@ -364,16 +613,57 @@ index fe2a14c40..aa01dc7e9 100644
      - name: build
        if: ${{ startsWith(matrix.cfg.testsuite, 'system-test') }}
        run: sudo -E ./.ci/ci.sh --archive-logs
-@@ -225,7 +289,7 @@ jobs:
+@@ -174,7 +246,7 @@ jobs:
+ 
+     - name: upload logs on failure
+       if: failure() || cancelled()
+-      uses: actions/upload-artifact@v3
++      uses: actions/upload-artifact@v4
+       with:
+         name: logs-linux-${{ join(matrix.cfg.*, '-') }}
+         path: logs.tgz
+@@ -193,18 +265,18 @@ jobs:
+     steps:
+     - name: checkout
+       if: github.event_name == 'push' || github.event_name == 'pull_request'
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+         submodules: recursive
+     # For weekly runs, don't update submodules
+     - name: checkout without submodule
+       if: github.event_name == 'schedule'
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+     # Weekly runs test using the tip of the most recent stable OVS branch
+     # instead of the submodule.
+     - name: checkout OVS
+       if: github.event_name == 'schedule'
+-      uses: actions/checkout@v3
++      uses: actions/checkout@v4
+       with:
+         repository: 'openvswitch/ovs'
+         fetch-depth: 0
+@@ -223,24 +295,24 @@ jobs:
+         echo "$HOME/bin"        >> $GITHUB_PATH
+         echo "$HOME/.local/bin" >> $GITHUB_PATH
      - name: set up python
-       uses: actions/setup-python@v4
+-      uses: actions/setup-python@v4
++      uses: actions/setup-python@v5
        with:
 -        python-version: '3.x'
 +        python-version: '3.12'
      - name: prepare
        run:  ./.ci/osx-prepare.sh
      - name: build
-@@ -239,8 +303,8 @@ jobs:
+       run:  ./.ci/osx-build.sh
+     - name: upload logs on failure
+       if: failure()
+-      uses: actions/upload-artifact@v3
++      uses: actions/upload-artifact@v4
+       with:
+         name: logs-osx-clang---disable-ssl
+         path: config.log
  
    build-linux-rpm:
      name: linux rpm fedora
@@ -384,6 +674,24 @@ index fe2a14c40..aa01dc7e9 100644
      timeout-minutes: 30
  
      strategy:
+@@ -251,7 +323,7 @@ jobs:
+         run: dnf install -y dnf-plugins-core git rpm-build
+ 
+       - name: checkout
+-        uses: actions/checkout@v3
++        uses: actions/checkout@v4
+         with:
+           submodules: recursive
+ 
+@@ -279,7 +351,7 @@ jobs:
+         run:  make rpm-fedora
+ 
+       - name: upload rpm packages
+-        uses: actions/upload-artifact@v3
++        uses: actions/upload-artifact@v4
+         with:
+           name: rpm-packages
+           path: |
 diff --git a/.readthedocs.yaml b/.readthedocs.yaml
 new file mode 100644
 index 000000000..8c451663a
@@ -543,6 +851,36 @@ index 77130c6e0..63a7997f7 100644
 -ovs_sphinx_theme>=1.0,<1.1
 +sphinx>=1.1
 +sphinx_rtd_theme>=1.0,<2.0
+diff --git a/Documentation/tutorials/ovn-sandbox.rst b/Documentation/tutorials/ovn-sandbox.rst
+index 2b574c02f..decc8abb3 100644
+--- a/Documentation/tutorials/ovn-sandbox.rst
++++ b/Documentation/tutorials/ovn-sandbox.rst
+@@ -162,16 +162,16 @@ to OpenFlow flows programmed by ``ovn-controller``.  See the `ovn-trace(8)`_
+ man page for more detail.
+ 
+ 
+-.. _ovn-architecture: http://openvswitch.org/support/dist-docs/ovn-architecture.7.html
+-.. _ovn-nb(5): http://openvswitch.org/support/dist-docs/ovn-nb.5.html
+-.. _ovn-sb(5): http://openvswitch.org/support/dist-docs/ovn-sb.5.html
++.. _ovn-architecture: http://www.ovn.org/support/dist-docs/ovn-architecture.7.html
++.. _ovn-nb(5): http://www.ovn.org/support/dist-docs/ovn-nb.5.html
++.. _ovn-sb(5): http://www.ovn.org/support/dist-docs/ovn-sb.5.html
+ .. _vtep(5): http://openvswitch.org/support/dist-docs/vtep.5.html
+-.. _ovn-northd(8): http://openvswitch.org/support/dist-docs/ovn-northd.8.html
+-.. _ovn-controller(8): http://openvswitch.org/support/dist-docs/ovn-controller.8.html
+-.. _ovn-controller-vtep(8): http://openvswitch.org/support/dist-docs/ovn-controller-vtep.8.html
++.. _ovn-northd(8): http://www.ovn.org/support/dist-docs/ovn-northd.8.html
++.. _ovn-controller(8): http://www.ovn.org/support/dist-docs/ovn-controller.8.html
++.. _ovn-controller-vtep(8): http://www.ovn.org/support/dist-docs/ovn-controller-vtep.8.html
+ .. _vtep-ctl(8): http://openvswitch.org/support/dist-docs/vtep-ctl.8.html
+-.. _ovn-nbctl(8): http://openvswitch.org/support/dist-docs/ovn-nbctl.8.html
+-.. _ovn-sbctl(8): http://openvswitch.org/support/dist-docs/ovn-sbctl.8.html
+-.. _ovn-trace(8): http://openvswitch.org/support/dist-docs/ovn-trace.8.html
++.. _ovn-nbctl(8): http://www.ovn.org/support/dist-docs/ovn-nbctl.8.html
++.. _ovn-sbctl(8): http://www.ovn.org/support/dist-docs/ovn-sbctl.8.html
++.. _ovn-trace(8): http://www.ovn.org/support/dist-docs/ovn-trace.8.html
+ .. _ovs-advanced: https://github.com/openvswitch/ovs/blob/master/Documentation/tutorials/ovs-advanced.rst
+ 
 diff --git a/Makefile.am b/Makefile.am
 index b58d4a501..bfc9565e8 100644
 --- a/Makefile.am
@@ -581,13 +919,17 @@ index b58d4a501..bfc9565e8 100644
  	  touch $@
  endif
 diff --git a/NEWS b/NEWS
-index 75e046ab3..ed74d0f38 100644
+index 75e046ab3..58b1c9066 100644
 --- a/NEWS
 +++ b/NEWS
-@@ -1,3 +1,11 @@
-+OVN v23.09.2 - xx xxx xxxx
+@@ -1,3 +1,15 @@
++OVN v23.09.3 - xx xxx xxxx
 +--------------------------
 +
++OVN v23.09.2 - 01 Mar 2024
++--------------------------
++  - Bug fixes
++  - Enable PMTU discovery on geneve/vxlan tunnels for E/W traffic.
 +
 +OVN v23.09.1 - 01 Dec 2023
 +--------------------------
@@ -597,7 +939,7 @@ index 75e046ab3..ed74d0f38 100644
  ----------------------------
    - Added FDB aging mechanism, that is disabled by default.
 diff --git a/configure.ac b/configure.ac
-index ab404b959..e4d5134dd 100644
+index ab404b959..090a29a15 100644
 --- a/configure.ac
 +++ b/configure.ac
 @@ -13,7 +13,7 @@
@@ -605,7 +947,7 @@ index ab404b959..e4d5134dd 100644
  
  AC_PREREQ(2.63)
 -AC_INIT(ovn, 23.09.0, bugs@openvswitch.org)
-+AC_INIT(ovn, 23.09.2, bugs@openvswitch.org)
++AC_INIT(ovn, 23.09.3, bugs@openvswitch.org)
  AC_CONFIG_MACRO_DIR([m4])
  AC_CONFIG_AUX_DIR([build-aux])
  AC_CONFIG_HEADERS([config.h])
@@ -626,6 +968,20 @@ index 23b368179..4472e5285 100644
              vtep_remote = xstrdup(optarg);
              break;
  
+diff --git a/controller/bfd.c b/controller/bfd.c
+index cf011e382..f47333191 100644
+--- a/controller/bfd.c
++++ b/controller/bfd.c
+@@ -235,6 +235,9 @@ bfd_run(const struct ovsrec_interface_table *interface_table,
+         if (mult) {
+             smap_add(&bfd, "mult", mult);
+         }
++        /* `check_tnl_key` must always be set to "true" to avoid processing of
++         * BFD control messages originating from a logical port. */
++        smap_add(&bfd, "check_tnl_key", "true");
+     }
+ 
+     /* Enable or disable bfd */
 diff --git a/controller/binding.c b/controller/binding.c
 index a521f2828..2afc5d48a 100644
 --- a/controller/binding.c
@@ -1290,18 +1646,19 @@ index 24bc84079..75e7a2679 100644
 +                        const struct uuid *pb_uuid);
  #endif /* controller/binding.h */
 diff --git a/controller/chassis.c b/controller/chassis.c
-index 031bd4463..a6f13ccc4 100644
+index 031bd4463..ba2e57238 100644
 --- a/controller/chassis.c
 +++ b/controller/chassis.c
-@@ -369,6 +369,7 @@ chassis_build_other_config(const struct ovs_chassis_cfg *ovs_cfg,
+@@ -369,6 +369,8 @@ chassis_build_other_config(const struct ovs_chassis_cfg *ovs_cfg,
      smap_replace(config, OVN_FEATURE_MAC_BINDING_TIMESTAMP, "true");
      smap_replace(config, OVN_FEATURE_CT_LB_RELATED, "true");
      smap_replace(config, OVN_FEATURE_FDB_TIMESTAMP, "true");
 +    smap_replace(config, OVN_FEATURE_LS_DPG_COLUMN, "true");
++    smap_replace(config, OVN_FEATURE_CT_COMMIT_NAT_V2, "true");
  }
  
  /*
-@@ -502,6 +503,12 @@ chassis_other_config_changed(const struct ovs_chassis_cfg *ovs_cfg,
+@@ -502,6 +504,18 @@ chassis_other_config_changed(const struct ovs_chassis_cfg *ovs_cfg,
          return true;
      }
  
@@ -1311,14 +1668,21 @@ index 031bd4463..a6f13ccc4 100644
 +        return true;
 +    }
 +
++    if (!smap_get_bool(&chassis_rec->other_config,
++                       OVN_FEATURE_CT_COMMIT_NAT_V2,
++                       false)) {
++        return true;
++    }
++
      return false;
  }
  
-@@ -632,6 +639,7 @@ update_supported_sset(struct sset *supported)
+@@ -632,6 +646,8 @@ update_supported_sset(struct sset *supported)
      sset_add(supported, OVN_FEATURE_MAC_BINDING_TIMESTAMP);
      sset_add(supported, OVN_FEATURE_CT_LB_RELATED);
      sset_add(supported, OVN_FEATURE_FDB_TIMESTAMP);
 +    sset_add(supported, OVN_FEATURE_LS_DPG_COLUMN);
++    sset_add(supported, OVN_FEATURE_CT_COMMIT_NAT_V2);
  }
  
  static void
@@ -1941,6 +2305,100 @@ index 644c67255..2f72aef5e 100644
  enum can_bind {
      CANNOT_BIND = 0,
      CAN_BIND_AS_MAIN,
+diff --git a/controller/ofctrl.c b/controller/ofctrl.c
+index a1676a788..1f06f4e48 100644
+--- a/controller/ofctrl.c
++++ b/controller/ofctrl.c
+@@ -2259,18 +2259,29 @@ ofctrl_meter_bands_erase(struct ovn_extend_table_info *entry,
+     }
+ }
+ 
++static const struct sbrec_meter *
++sb_meter_lookup_by_name(struct ovsdb_idl_index *sbrec_meter_by_name,
++                        const char *name)
++{
++    const struct sbrec_meter *sb_meter;
++    struct sbrec_meter *index_row;
++
++    index_row = sbrec_meter_index_init_row(sbrec_meter_by_name);
++    sbrec_meter_index_set_name(index_row, name);
++    sb_meter = sbrec_meter_index_find(sbrec_meter_by_name, index_row);
++    sbrec_meter_index_destroy_row(index_row);
++
++    return sb_meter;
++}
++
+ static void
+ ofctrl_meter_bands_sync(struct ovn_extend_table_info *m_existing,
+-                        const struct sbrec_meter_table *meter_table,
++                        struct ovsdb_idl_index *sbrec_meter_by_name,
+                         struct ovs_list *msgs)
+ {
+     const struct sbrec_meter *sb_meter;
+-    SBREC_METER_TABLE_FOR_EACH (sb_meter, meter_table) {
+-        if (!strcmp(m_existing->name, sb_meter->name)) {
+-            break;
+-        }
+-    }
+ 
++    sb_meter = sb_meter_lookup_by_name(sbrec_meter_by_name, m_existing->name);
+     if (sb_meter) {
+         /* OFPMC13_ADD or OFPMC13_MODIFY */
+         ofctrl_meter_bands_update(sb_meter, m_existing, msgs);
+@@ -2282,16 +2293,12 @@ ofctrl_meter_bands_sync(struct ovn_extend_table_info *m_existing,
+ 
+ static void
+ add_meter(struct ovn_extend_table_info *m_desired,
+-          const struct sbrec_meter_table *meter_table,
++          struct ovsdb_idl_index *sbrec_meter_by_name,
+           struct ovs_list *msgs)
+ {
+     const struct sbrec_meter *sb_meter;
+-    SBREC_METER_TABLE_FOR_EACH (sb_meter, meter_table) {
+-        if (!strcmp(m_desired->name, sb_meter->name)) {
+-            break;
+-        }
+-    }
+ 
++    sb_meter = sb_meter_lookup_by_name(sbrec_meter_by_name, m_desired->name);
+     if (!sb_meter) {
+         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+         VLOG_ERR_RL(&rl, "could not find meter named \"%s\"", m_desired->name);
+@@ -2658,7 +2665,7 @@ ofctrl_put(struct ovn_desired_flow_table *lflow_table,
+            struct ovn_desired_flow_table *pflow_table,
+            struct shash *pending_ct_zones,
+            struct hmap *pending_lb_tuples,
+-           const struct sbrec_meter_table *meter_table,
++           struct ovsdb_idl_index *sbrec_meter_by_name,
+            uint64_t req_cfg,
+            bool lflows_changed,
+            bool pflows_changed)
+@@ -2735,10 +2742,10 @@ ofctrl_put(struct ovn_desired_flow_table *lflow_table,
+                  * describes the meter itself. */
+                 add_meter_string(m_desired, &msgs);
+             } else {
+-                add_meter(m_desired, meter_table, &msgs);
++                add_meter(m_desired, sbrec_meter_by_name, &msgs);
+             }
+         } else {
+-            ofctrl_meter_bands_sync(m_existing, meter_table, &msgs);
++            ofctrl_meter_bands_sync(m_existing, sbrec_meter_by_name, &msgs);
+         }
+     }
+ 
+diff --git a/controller/ofctrl.h b/controller/ofctrl.h
+index 105f9370b..bb7891440 100644
+--- a/controller/ofctrl.h
++++ b/controller/ofctrl.h
+@@ -59,7 +59,7 @@ void ofctrl_put(struct ovn_desired_flow_table *lflow_table,
+                 struct ovn_desired_flow_table *pflow_table,
+                 struct shash *pending_ct_zones,
+                 struct hmap *pending_lb_tuples,
+-                const struct sbrec_meter_table *,
++                struct ovsdb_idl_index *sbrec_meter_by_name,
+                 uint64_t nb_cfg,
+                 bool lflow_changed,
+                 bool pflow_changed);
 diff --git a/controller/ovn-controller.8.xml b/controller/ovn-controller.8.xml
 index 7b4100592..0b9641045 100644
 --- a/controller/ovn-controller.8.xml
@@ -1973,7 +2431,7 @@ index 7b4100592..0b9641045 100644
  
      <p>
 diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
-index b3e4e0da8..6787dda80 100644
+index b3e4e0da8..003490a06 100644
 --- a/controller/ovn-controller.c
 +++ b/controller/ovn-controller.c
 @@ -88,7 +88,6 @@
@@ -2241,7 +2699,16 @@ index b3e4e0da8..6787dda80 100644
                               &exit_args);
  
      daemonize_complete();
-@@ -5508,10 +5561,8 @@ main(int argc, char *argv[])
+@@ -5049,6 +5102,8 @@ main(int argc, char *argv[])
+         = chassis_private_index_create(ovnsb_idl_loop.idl);
+     struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath
+         = mcast_group_index_create(ovnsb_idl_loop.idl);
++    struct ovsdb_idl_index *sbrec_meter_by_name
++        = ovsdb_idl_index_create1(ovnsb_idl_loop.idl, &sbrec_meter_col_name);
+     struct ovsdb_idl_index *sbrec_logical_flow_by_logical_datapath
+         = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
+                                   &sbrec_logical_flow_col_logical_datapath);
+@@ -5508,10 +5563,8 @@ main(int argc, char *argv[])
      VLOG_INFO("OVN internal version is : [%s]", ovn_version);
  
      /* Main loop. */
@@ -2253,7 +2720,7 @@ index b3e4e0da8..6787dda80 100644
          memory_run();
          if (memory_should_report()) {
              struct simap usage = SIMAP_INITIALIZER(&usage);
-@@ -5645,9 +5696,20 @@ main(int argc, char *argv[])
+@@ -5645,9 +5698,20 @@ main(int argc, char *argv[])
                                             br_int ? br_int->name : NULL)) {
                  VLOG_INFO("OVS feature set changed, force recompute.");
                  engine_set_force_recompute(true);
@@ -2275,7 +2742,7 @@ index b3e4e0da8..6787dda80 100644
                  ct_zones_data = engine_get_data(&en_ct_zones);
                  if (ct_zones_data && ofctrl_run(br_int, ovs_table,
                                                  &ct_zones_data->pending)) {
-@@ -5809,6 +5871,13 @@ main(int argc, char *argv[])
+@@ -5809,6 +5873,13 @@ main(int argc, char *argv[])
                                      &runtime_data->local_datapaths,
                                      sb_monitor_all);
                          }
@@ -2289,7 +2756,16 @@ index b3e4e0da8..6787dda80 100644
                      }
  
                      if (mac_cache_data) {
-@@ -5864,6 +5933,8 @@ main(int argc, char *argv[])
+@@ -5848,7 +5919,7 @@ main(int argc, char *argv[])
+                                    &pflow_output_data->flow_table,
+                                    &ct_zones_data->pending,
+                                    &lb_data->removed_tuples,
+-                                   sbrec_meter_table_get(ovnsb_idl_loop.idl),
++                                   sbrec_meter_by_name,
+                                    ofctrl_seqno_get_req_cfg(),
+                                    engine_node_changed(&en_lflow_output),
+                                    engine_node_changed(&en_pflow_output));
+@@ -5864,6 +5935,8 @@ main(int argc, char *argv[])
                      if_status_mgr_run(if_mgr, binding_data, chassis,
                                        ovsrec_interface_table_get(
                                                    ovs_idl_loop.idl),
@@ -2298,7 +2774,7 @@ index b3e4e0da8..6787dda80 100644
                                        !ovnsb_idl_txn, !ovs_idl_txn);
                      stopwatch_stop(IF_STATUS_MGR_RUN_STOPWATCH_NAME,
                                     time_msec());
-@@ -5941,7 +6012,7 @@ main(int argc, char *argv[])
+@@ -5941,7 +6014,7 @@ main(int argc, char *argv[])
          unixctl_server_run(unixctl);
  
          unixctl_server_wait(unixctl);
@@ -2307,7 +2783,7 @@ index b3e4e0da8..6787dda80 100644
              poll_immediate_wake();
          }
  
-@@ -5992,7 +6063,7 @@ loop_done:
+@@ -5992,7 +6065,7 @@ loop_done:
          memory_wait();
          poll_block();
          if (should_service_stop()) {
@@ -2316,7 +2792,7 @@ index b3e4e0da8..6787dda80 100644
          }
      }
  
-@@ -6000,7 +6071,7 @@ loop_done:
+@@ -6000,7 +6073,7 @@ loop_done:
      engine_cleanup();
  
      /* It's time to exit.  Clean up the databases if we are not restarting */
@@ -2325,7 +2801,7 @@ index b3e4e0da8..6787dda80 100644
          bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl);
          while (!done) {
              update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl,
-@@ -6052,7 +6123,6 @@ loop_done:
+@@ -6052,7 +6125,6 @@ loop_done:
      }
  
      free(ovn_version);
@@ -2333,7 +2809,7 @@ index b3e4e0da8..6787dda80 100644
      lflow_destroy();
      ofctrl_destroy();
      pinctrl_destroy();
-@@ -6077,6 +6147,8 @@ loop_done:
+@@ -6077,6 +6149,8 @@ loop_done:
      if (cli_system_id) {
          free(cli_system_id);
      }
@@ -2342,7 +2818,21 @@ index b3e4e0da8..6787dda80 100644
      service_stop();
      ovsrcu_exit();
  
-@@ -6156,6 +6228,7 @@ parse_options(int argc, char *argv[])
+@@ -6142,6 +6216,13 @@ parse_options(int argc, char *argv[])
+             ssl_ca_cert_file = optarg;
+             break;
+ 
++        case OPT_SSL_PROTOCOLS:
++            stream_ssl_set_protocols(optarg);
++            break;
++
++        case OPT_SSL_CIPHERS:
++            stream_ssl_set_ciphers(optarg);
++            break;
+ 
+         case OPT_PEER_CA_CERT:
+             stream_ssl_set_peer_ca_cert_file(optarg);
+@@ -6156,6 +6237,7 @@ parse_options(int argc, char *argv[])
              break;
  
          case 'n':
@@ -2350,7 +2840,7 @@ index b3e4e0da8..6787dda80 100644
              cli_system_id = xstrdup(optarg);
              break;
  
-@@ -6200,16 +6273,6 @@ usage(void)
+@@ -6200,16 +6282,6 @@ usage(void)
      exit(EXIT_SUCCESS);
  }
  
@@ -2368,7 +2858,7 @@ index b3e4e0da8..6787dda80 100644
  ct_zone_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
               const char *argv[] OVS_UNUSED, void *ct_zones_)
 diff --git a/controller/physical.c b/controller/physical.c
-index 75257bc85..3b862ca34 100644
+index 75257bc85..dbce84c4d 100644
 --- a/controller/physical.c
 +++ b/controller/physical.c
 @@ -341,7 +341,7 @@ get_remote_tunnels(const struct sbrec_port_binding *binding,
@@ -2430,8 +2920,47 @@ index 75257bc85..3b862ca34 100644
      }
  
      struct ofpact_push_vlan *push_vlan;
+@@ -2371,9 +2382,37 @@ physical_run(struct physical_ctx *p_ctx,
+         }
+ 
+         put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
+-
+         ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
+                         &ofpacts, hc_uuid);
++
++        /* Set allow rx from tunnel bit. */
++        put_load(1, MFF_LOG_FLAGS, MLF_RX_FROM_TUNNEL_BIT, 1, &ofpacts);
++
++        /* Add specif flows for E/W ICMPv{4,6} packets if tunnelled packets
++         * do not fit path MTU.
++         */
++        put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
++
++        /* IPv4 */
++        match_init_catchall(&match);
++        match_set_in_port(&match, tun->ofport);
++        match_set_dl_type(&match, htons(ETH_TYPE_IP));
++        match_set_nw_proto(&match, IPPROTO_ICMP);
++        match_set_icmp_type(&match, 3);
++        match_set_icmp_code(&match, 4);
++
++        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 120, 0, &match,
++                        &ofpacts, hc_uuid);
++        /* IPv6 */
++        match_init_catchall(&match);
++        match_set_in_port(&match, tun->ofport);
++        match_set_dl_type(&match, htons(ETH_TYPE_IPV6));
++        match_set_nw_proto(&match, IPPROTO_ICMPV6);
++        match_set_icmp_type(&match, 2);
++        match_set_icmp_code(&match, 0);
++
++        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 120, 0, &match,
++                        &ofpacts, hc_uuid);
+     }
+ 
+     /* Add VXLAN specific rules to transform port keys
 diff --git a/controller/pinctrl.c b/controller/pinctrl.c
-index ff5a3444c..ececbdb48 100644
+index ff5a3444c..79b00c878 100644
 --- a/controller/pinctrl.c
 +++ b/controller/pinctrl.c
 @@ -373,9 +373,13 @@ static const struct sbrec_fdb *fdb_lookup(
@@ -2478,7 +3007,75 @@ index ff5a3444c..ececbdb48 100644
          }
  
          pfd = shash_find_data(&ipv6_prefixd, pb->logical_port);
-@@ -3577,7 +3595,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+@@ -2850,6 +2868,8 @@ dns_build_ptr_answer(
+     free(encoded);
+ }
+ 
++#define DNS_QUERY_TYPE_CLASS_LEN (2 * sizeof(ovs_be16))
++
+ /* Called with in the pinctrl_handler thread context. */
+ static void
+ pinctrl_handle_dns_lookup(
+@@ -2911,18 +2931,13 @@ pinctrl_handle_dns_lookup(
+         goto exit;
+     }
+ 
+-    /* Check if there is an additional record present, which is unsupported */
+-    if (in_dns_header->arcount) {
+-        VLOG_DBG_RL(&rl, "Received DNS query with additional records, which"
+-                    " is unsupported");
+-        goto exit;
+-    }
+-
+     struct udp_header *in_udp = dp_packet_l4(pkt_in);
+     size_t udp_len = ntohs(in_udp->udp_len);
+     size_t l4_len = dp_packet_l4_size(pkt_in);
++    uint8_t *l4_start = (uint8_t *) in_udp;
+     uint8_t *end = (uint8_t *)in_udp + MIN(udp_len, l4_len);
+     uint8_t *in_dns_data = (uint8_t *)(in_dns_header + 1);
++    uint8_t *in_dns_data_start = in_dns_data;
+     uint8_t *in_queryname = in_dns_data;
+     uint16_t idx = 0;
+     struct ds query_name;
+@@ -2946,7 +2961,7 @@ pinctrl_handle_dns_lookup(
+     in_dns_data += idx;
+ 
+     /* Query should have TYPE and CLASS fields */
+-    if (in_dns_data + (2 * sizeof(ovs_be16)) > end) {
++    if (in_dns_data + DNS_QUERY_TYPE_CLASS_LEN > end) {
+         ds_destroy(&query_name);
+         goto exit;
+     }
+@@ -2960,6 +2975,10 @@ pinctrl_handle_dns_lookup(
+         goto exit;
+     }
+ 
++    uint8_t *rest = in_dns_data + DNS_QUERY_TYPE_CLASS_LEN;
++    uint32_t query_size = rest - in_dns_data_start;
++    uint32_t query_l4_size = rest - l4_start;
++
+     uint64_t dp_key = ntohll(pin->flow_metadata.flow.metadata);
+     const char *answer_data = NULL;
+     struct shash_node *iter;
+@@ -3028,7 +3047,7 @@ pinctrl_handle_dns_lookup(
+         goto exit;
+     }
+ 
+-    uint16_t new_l4_size = ntohs(in_udp->udp_len) +  dns_answer.size;
++    uint16_t new_l4_size = query_l4_size + dns_answer.size;
+     size_t new_packet_size = pkt_in->l4_ofs + new_l4_size;
+     struct dp_packet pkt_out;
+     dp_packet_init(&pkt_out, new_packet_size);
+@@ -3061,7 +3080,7 @@ pinctrl_handle_dns_lookup(
+     out_dns_header->arcount = 0;
+ 
+     /* Copy the Query section. */
+-    dp_packet_put(&pkt_out, dp_packet_data(pkt_in), dp_packet_size(pkt_in));
++    dp_packet_put(&pkt_out, dp_packet_data(pkt_in), query_size);
+ 
+     /* Copy the answer sections. */
+     dp_packet_put(&pkt_out, dns_answer.data, dns_answer.size);
+@@ -3577,7 +3596,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                        chassis);
      bfd_monitor_run(ovnsb_idl_txn, bfd_table, sbrec_port_binding_by_name,
                      chassis, active_tunnels);
@@ -2488,7 +3085,7 @@ index ff5a3444c..ececbdb48 100644
      run_activated_ports(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
                          sbrec_port_binding_by_key, chassis);
      ovs_mutex_unlock(&pinctrl_mutex);
-@@ -6226,6 +6245,12 @@ pinctrl_handle_put_nd_ra_opts(
+@@ -6226,6 +6246,12 @@ pinctrl_handle_put_nd_ra_opts(
  
      /* Set the IPv6 payload length and calculate the ICMPv6 checksum. */
      struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
@@ -2501,7 +3098,7 @@ index ff5a3444c..ececbdb48 100644
      nh->ip6_plen = htons(userdata->size);
      struct ovs_ra_msg *ra = dp_packet_l4(&pkt_out);
      ra->icmph.icmp6_cksum = 0;
-@@ -6338,26 +6363,31 @@ pinctrl_handle_empty_lb_backends_opts(struct ofpbuf *userdata)
+@@ -6338,26 +6364,31 @@ pinctrl_handle_empty_lb_backends_opts(struct ofpbuf *userdata)
      char *protocol = NULL;
      char *load_balancer = NULL;
  
@@ -2535,7 +3132,7 @@ index ff5a3444c..ececbdb48 100644
              load_balancer = xmemdup0(userdata_opt_data, size);
              break;
          default:
-@@ -6368,36 +6398,39 @@ pinctrl_handle_empty_lb_backends_opts(struct ofpbuf *userdata)
+@@ -6368,36 +6399,39 @@ pinctrl_handle_empty_lb_backends_opts(struct ofpbuf *userdata)
      if (!vip || !protocol || !load_balancer) {
          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
          VLOG_WARN_RL(&rl, "missing lb parameters in userdata");
@@ -2599,7 +3196,7 @@ index ff5a3444c..ececbdb48 100644
  }
  
  static void
-@@ -7755,7 +7788,9 @@ svc_monitors_run(struct rconn *swconn,
+@@ -7755,7 +7789,9 @@ svc_monitors_run(struct rconn *swconn,
              if (svc_mon->n_success >= svc_mon->success_count) {
                  svc_mon->status = SVC_MON_ST_ONLINE;
                  svc_mon->n_success = 0;
@@ -2609,7 +3206,7 @@ index ff5a3444c..ececbdb48 100644
              if (current_time >= svc_mon->next_send_time) {
                  svc_monitor_send_health_check(swconn, svc_mon);
                  next_run_time = svc_mon->wait_time;
-@@ -7767,6 +7802,7 @@ svc_monitors_run(struct rconn *swconn,
+@@ -7767,6 +7803,7 @@ svc_monitors_run(struct rconn *swconn,
          case SVC_MON_S_OFFLINE:
              if (svc_mon->n_failures >= svc_mon->failure_count) {
                  svc_mon->status = SVC_MON_ST_OFFLINE;
@@ -2617,7 +3214,7 @@ index ff5a3444c..ececbdb48 100644
                  svc_mon->n_failures = 0;
              }
  
-@@ -7812,7 +7848,6 @@ pinctrl_handle_tcp_svc_check(struct rconn *swconn,
+@@ -7812,7 +7849,6 @@ pinctrl_handle_tcp_svc_check(struct rconn *swconn,
          return false;
      }
  
@@ -2625,7 +3222,7 @@ index ff5a3444c..ececbdb48 100644
      uint32_t tcp_ack = ntohl(get_16aligned_be32(&th->tcp_ack));
  
      if (th->tcp_dst != svc_mon->tp_src) {
-@@ -7829,10 +7864,10 @@ pinctrl_handle_tcp_svc_check(struct rconn *swconn,
+@@ -7829,10 +7865,10 @@ pinctrl_handle_tcp_svc_check(struct rconn *swconn,
          svc_mon->n_success++;
          svc_mon->state = SVC_MON_S_ONLINE;
  
@@ -2640,7 +3237,7 @@ index ff5a3444c..ececbdb48 100644
          /* Calculate next_send_time. */
          svc_mon->next_send_time = time_msec() + svc_mon->interval;
          return true;
-@@ -8184,6 +8219,8 @@ fdb_lookup(struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac, uint32_t dp_key,
+@@ -8184,6 +8220,8 @@ fdb_lookup(struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac, uint32_t dp_key,
  static void
  run_put_fdb(struct ovsdb_idl_txn *ovnsb_idl_txn,
              struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac,
@@ -2649,7 +3246,7 @@ index ff5a3444c..ececbdb48 100644
              const struct fdb_entry *fdb_e)
  {
      /* Convert ethernet argument to string form for database. */
-@@ -8192,14 +8229,28 @@ run_put_fdb(struct ovsdb_idl_txn *ovnsb_idl_txn,
+@@ -8192,14 +8230,28 @@ run_put_fdb(struct ovsdb_idl_txn *ovnsb_idl_txn,
               ETH_ADDR_FMT, ETH_ADDR_ARGS(fdb_e->mac));
  
      /* Update or add an FDB entry. */
@@ -2679,7 +3276,7 @@ index ff5a3444c..ececbdb48 100644
  
      /* For backward compatibility check if timestamp column is available
       * in SB DB. */
-@@ -8210,6 +8261,8 @@ run_put_fdb(struct ovsdb_idl_txn *ovnsb_idl_txn,
+@@ -8210,6 +8262,8 @@ run_put_fdb(struct ovsdb_idl_txn *ovnsb_idl_txn,
  
  static void
  run_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn,
@@ -2688,7 +3285,7 @@ index ff5a3444c..ececbdb48 100644
               struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac)
               OVS_REQUIRES(pinctrl_mutex)
  {
-@@ -8219,7 +8272,9 @@ run_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn,
+@@ -8219,7 +8273,9 @@ run_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn,
  
      const struct fdb_entry *fdb_e;
      HMAP_FOR_EACH (fdb_e, hmap_node, &put_fdbs) {
@@ -2713,15 +3310,21 @@ index cb1545cbb..8cce97df8 100644
  
      while (!latch_is_set(&ctx->exit_latch)) {
 diff --git a/debian/changelog b/debian/changelog
-index 96d132784..479d4c944 100644
+index 96d132784..d61c4a6ef 100644
 --- a/debian/changelog
 +++ b/debian/changelog
-@@ -1,3 +1,15 @@
+@@ -1,3 +1,21 @@
++OVN (23.09.3-1) unstable; urgency=low
++   [ OVN team ]
++   * New upstream version
++
++ -- OVN team <dev@openvswitch.org>  Fri, 01 Mar 2024 14:06:41 -0500
++
 +OVN (23.09.2-1) unstable; urgency=low
 +   [ OVN team ]
 +   * New upstream version
 +
-+ -- OVN team <dev@openvswitch.org>  Fri, 01 Dec 2023 14:41:31 -0500
++ -- OVN team <dev@openvswitch.org>  Fri, 01 Mar 2024 14:06:41 -0500
 +
 +OVN (23.09.1-1) unstable; urgency=low
 +   [ OVN team ]
@@ -2733,10 +3336,54 @@ index 96d132784..479d4c944 100644
  
     * New upstream version
 diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
-index e2023c2ba..020b1d60b 100644
+index e2023c2ba..6db6ce839 100644
 --- a/ic/ovn-ic.c
 +++ b/ic/ovn-ic.c
-@@ -1630,13 +1630,18 @@ collect_lr_routes(struct ic_context *ctx,
+@@ -132,14 +132,18 @@ az_run(struct ic_context *ctx)
+         return NULL;
+     }
+ 
+-    /* Delete old AZ if name changes.  Note: if name changed when ovn-ic
+-     * is not running, one has to manually delete the old AZ with:
++    /* Update old AZ if name changes.  Note: if name changed when ovn-ic
++     * is not running, one has to manually delete/update the old AZ with:
+      * "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)) {
+         ICSBREC_AVAILABILITY_ZONE_FOR_EACH (az, ctx->ovnisb_idl) {
+-            if (!strcmp(az->name, az_name)) {
++            /* AZ name update locally need to update az in ISB. */
++            if (nb_global->name[0] && !strcmp(az->name, az_name)) {
++                icsbrec_availability_zone_set_name(az, nb_global->name);
++                break;
++            } else if (!nb_global->name[0] && !strcmp(az->name, az_name)) {
+                 icsbrec_availability_zone_delete(az);
+                 break;
+             }
+@@ -1064,12 +1068,15 @@ prefix_is_black_listed(const struct smap *nb_options,
+                 continue;
+             }
+         } else {
+-            struct in6_addr mask = ipv6_create_mask(bl_plen);
+-            for (int i = 0; i < 16 && mask.s6_addr[i] != 0; i++) {
+-                if ((prefix->s6_addr[i] & mask.s6_addr[i])
+-                    != (bl_prefix.s6_addr[i] & mask.s6_addr[i])) {
+-                    continue;
+-                }
++            struct in6_addr mask = ipv6_create_mask(plen);
++            /* First calculate the difference between bl_prefix and prefix, so
++             * use the bl mask to ensure prefixes are correctly validated.
++             * e.g.: 2005:1734:5678::/50 is a subnet of 2005:1234::/21 */
++            struct in6_addr m_prefixes = ipv6_addr_bitand(prefix, &bl_prefix);
++            struct in6_addr m_prefix = ipv6_addr_bitand(&m_prefixes, &mask);
++            struct in6_addr m_bl_prefix = ipv6_addr_bitand(&bl_prefix, &mask);
++            if (!ipv6_addr_equals(&m_prefix, &m_bl_prefix)) {
++                continue;
+             }
+         }
+         matched = true;
+@@ -1630,13 +1637,18 @@ collect_lr_routes(struct ic_context *ctx,
      const struct icnbrec_transit_switch *key;
  
      struct hmap *routes_ad;
@@ -2757,7 +3404,22 @@ index e2023c2ba..020b1d60b 100644
          icnbrec_transit_switch_index_destroy_row(key);
          routes_ad = shash_find_data(routes_ad_by_ts, ts_name);
          if (!routes_ad) {
-@@ -2216,10 +2221,19 @@ main(int argc, char *argv[])
+@@ -1841,6 +1853,14 @@ parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+             ssl_ca_cert_file = optarg;
+             break;
+ 
++        case OPT_SSL_PROTOCOLS:
++            stream_ssl_set_protocols(optarg);
++            break;
++
++        case OPT_SSL_CIPHERS:
++            stream_ssl_set_ciphers(optarg);
++            break;
++
+         case 'd':
+             ovnsb_db = optarg;
+             break;
+@@ -2216,10 +2236,19 @@ main(int argc, char *argv[])
                  ovn_db_run(&ctx);
              }
  
@@ -2781,19 +3443,57 @@ index e2023c2ba..020b1d60b 100644
          } else {
              /* ovn-ic is paused
               *    - we still want to handle any db updates and update the
+diff --git a/include/ovn/actions.h b/include/ovn/actions.h
+index 04bb6ffd0..b99c086e3 100644
+--- a/include/ovn/actions.h
++++ b/include/ovn/actions.h
+@@ -75,7 +75,7 @@ struct collector_set_ids;
+     OVNACT(CT_LB_MARK,        ovnact_ct_lb)           \
+     OVNACT(SELECT,            ovnact_select)          \
+     OVNACT(CT_CLEAR,          ovnact_null)            \
+-    OVNACT(CT_COMMIT_NAT,     ovnact_ct_nat)          \
++    OVNACT(CT_COMMIT_NAT,     ovnact_ct_commit_nat)   \
+     OVNACT(CLONE,             ovnact_nest)            \
+     OVNACT(ARP,               ovnact_nest)            \
+     OVNACT(ICMP4,             ovnact_nest)            \
+@@ -274,7 +274,7 @@ enum ovnact_ct_nat_type {
+     OVNACT_CT_NAT_UNSPEC,
+ };
+ 
+-/* OVNACT_CT_DNAT, OVNACT_CT_SNAT, OVNACT_CT_COMMIT_NAT. */
++/* OVNACT_CT_DNAT, OVNACT_CT_SNAT. */
+ struct ovnact_ct_nat {
+     struct ovnact ovnact;
+     int family;
+@@ -296,6 +296,14 @@ struct ovnact_ct_nat {
+     uint8_t ltable;             /* Logical table ID of next table. */
+ };
+ 
++/* OVNACT_CT_COMMIT_NAT. */
++struct ovnact_ct_commit_nat {
++    struct ovnact ovnact;
++
++    bool dnat_zone;
++    uint8_t ltable;
++};
++
+ enum ovnact_ct_lb_flag {
+     OVNACT_CT_LB_FLAG_NONE,
+     OVNACT_CT_LB_FLAG_SKIP_SNAT,
 diff --git a/include/ovn/features.h b/include/ovn/features.h
-index 3bf536127..2c47ab766 100644
+index 3bf536127..08f1d8288 100644
 --- a/include/ovn/features.h
 +++ b/include/ovn/features.h
-@@ -26,6 +26,7 @@
+@@ -26,6 +26,8 @@
  #define OVN_FEATURE_MAC_BINDING_TIMESTAMP "mac-binding-timestamp"
  #define OVN_FEATURE_CT_LB_RELATED "ovn-ct-lb-related"
  #define OVN_FEATURE_FDB_TIMESTAMP "fdb-timestamp"
 +#define OVN_FEATURE_LS_DPG_COLUMN "ls-dpg-column"
++#define OVN_FEATURE_CT_COMMIT_NAT_V2 "ct-commit-nat-v2"
  
  /* OVS datapath supported features.  Based on availability OVN might generate
   * different types of openflows.
-@@ -48,5 +49,8 @@ void ovs_feature_support_destroy(void);
+@@ -48,5 +50,8 @@ void ovs_feature_support_destroy(void);
  bool ovs_feature_is_supported(enum ovs_feature_value feature);
  bool ovs_feature_support_run(const struct smap *ovs_capabilities,
                               const char *br_name);
@@ -2803,18 +3503,20 @@ index 3bf536127..2c47ab766 100644
  
  #endif
 diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
-index a7b64ef67..272277ec4 100644
+index a7b64ef67..f07c4c42e 100644
 --- a/include/ovn/logical-fields.h
 +++ b/include/ovn/logical-fields.h
-@@ -77,6 +77,7 @@ enum mff_log_flags_bits {
+@@ -77,6 +77,9 @@ enum mff_log_flags_bits {
      MLF_CHECK_PORT_SEC_BIT = 12,
      MLF_LOOKUP_COMMIT_ECMP_NH_BIT = 13,
      MLF_USE_LB_AFF_SESSION_BIT = 14,
 +    MLF_LOCALNET_BIT = 15,
++    MLF_RX_FROM_TUNNEL_BIT = 16,
++    MLF_ICMP_SNAT_BIT = 17,
  };
  
  /* MFF_LOG_FLAGS_REG flag assignments */
-@@ -124,6 +125,10 @@ enum mff_log_flags {
+@@ -124,6 +127,14 @@ enum mff_log_flags {
      MLF_LOOKUP_COMMIT_ECMP_NH = (1 << MLF_LOOKUP_COMMIT_ECMP_NH_BIT),
  
      MLF_USE_LB_AFF_SESSION = (1 << MLF_USE_LB_AFF_SESSION_BIT),
@@ -2822,14 +3524,143 @@ index a7b64ef67..272277ec4 100644
 +    /* Indicate that the port is localnet. */
 +    MLF_LOCALNET = (1 << MLF_LOCALNET_BIT),
 +
++    /* Indicate the packet has been received from the tunnel. */
++    MLF_RX_FROM_TUNNEL = (1 << MLF_RX_FROM_TUNNEL_BIT),
++
++    MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT),
  };
  
  /* OVN logical fields
 diff --git a/lib/actions.c b/lib/actions.c
-index b880927b6..4d408d82d 100644
+index b880927b6..1384672f5 100644
 --- a/lib/actions.c
 +++ b/lib/actions.c
-@@ -5004,6 +5004,7 @@ encode_COMMIT_LB_AFF(const struct ovnact_commit_lb_aff *lb_aff,
+@@ -1020,16 +1020,29 @@ parse_CT_COMMIT_NAT(struct action_context *ctx)
+ 
+     if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
+         lexer_error(ctx->lexer,
+-                    "\"ct_commit_related\" action not allowed in last table.");
++                    "\"ct_commit_nat\" action not allowed in last table.");
+         return;
+     }
+ 
+-    struct ovnact_ct_nat *cn = ovnact_put_CT_COMMIT_NAT(ctx->ovnacts);
+-    cn->commit = true;
++    struct ovnact_ct_commit_nat *cn = ovnact_put_CT_COMMIT_NAT(ctx->ovnacts);
+     cn->ltable = ctx->pp->cur_ltable + 1;
+-    cn->family = AF_UNSPEC;
+-    cn->type = OVNACT_CT_NAT_UNSPEC;
+-    cn->port_range.exists = false;
++    cn->dnat_zone = true;
++
++    if (!lexer_match(ctx->lexer, LEX_T_LPAREN)) {
++        return;
++    }
++
++    if (lexer_match_id(ctx->lexer, "dnat")) {
++        cn->dnat_zone = true;
++    } else if (lexer_match_id(ctx->lexer, "snat")) {
++        cn->dnat_zone = false;
++    } else {
++        lexer_error(ctx->lexer, "\"ct_commit_nat\" action accepts"
++                    " only \"dnat\" or \"snat\" parameter.");
++        return;
++    }
++
++    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+ }
+ 
+ static void
+@@ -1082,9 +1095,10 @@ format_CT_SNAT_IN_CZONE(const struct ovnact_ct_nat *cn, struct ds *s)
+ }
+ 
+ static void
+-format_CT_COMMIT_NAT(const struct ovnact_ct_nat *cn OVS_UNUSED, struct ds *s)
++format_CT_COMMIT_NAT(const struct ovnact_ct_commit_nat *cn, struct ds *s)
+ {
+-    ds_put_cstr(s, "ct_commit_nat;");
++    ds_put_cstr(s, "ct_commit_nat");
++    ds_put_cstr(s, cn->dnat_zone ? "(dnat);" : "(snat);");
+ }
+ 
+ static void
+@@ -1131,8 +1145,20 @@ encode_ct_nat(const struct ovnact_ct_nat *cn,
+     }
+ 
+     if (cn->port_range.exists) {
+-       nat->range.proto.min = cn->port_range.port_lo;
+-       nat->range.proto.max = cn->port_range.port_hi;
++        nat->range.proto.min = cn->port_range.port_lo;
++        nat->range.proto.max = cn->port_range.port_hi;
++
++        /* Explicitly set the port selection algorithm to "random".  Otherwise
++         * it's up to the datapath to choose how to select the port and that
++         * might create unexpected behavior changes when the datapath defaults
++         * change.
++         *
++         * NOTE: for the userspace datapath the "random" function doesn't
++         * really generate random ports, it uses "hash" under the hood:
++         * https://issues.redhat.com/browse/FDP-269. */
++        if (nat->range.proto.min && nat->range.proto.max) {
++            nat->flags |= NX_NAT_F_PROTO_RANDOM;
++        }
+     }
+ 
+     ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset);
+@@ -1177,20 +1203,45 @@ encode_CT_SNAT_IN_CZONE(const struct ovnact_ct_nat *cn,
+ }
+ 
+ static void
+-encode_CT_COMMIT_NAT(const struct ovnact_ct_nat *cn,
+-                         const struct ovnact_encode_params *ep,
+-                         struct ofpbuf *ofpacts)
++encode_CT_COMMIT_NAT(const struct ovnact_ct_commit_nat *cn,
++                     const struct ovnact_encode_params *ep,
++                     struct ofpbuf *ofpacts)
+ {
+-    enum mf_field_id zone = ep->is_switch
+-                            ? MFF_LOG_CT_ZONE
+-                            : MFF_LOG_DNAT_ZONE;
+-    encode_ct_nat(cn, ep, zone, ofpacts);
++    const size_t ct_offset = ofpacts->size;
++
++    struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts);
++    ct->recirc_table = cn->ltable + first_ptable(ep, ep->pipeline);
++    ct->zone_src.ofs = 0;
++    ct->zone_src.n_bits = 16;
++    ct->flags = NX_CT_F_COMMIT;
++    ct->alg = 0;
++
++    if (ep->is_switch) {
++        ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE);
++    } else {
++        ct->zone_src.field = mf_from_id(cn->dnat_zone
++                                        ? MFF_LOG_DNAT_ZONE
++                                        : MFF_LOG_SNAT_ZONE);
++    }
++
++    struct ofpact_nat *nat = ofpact_put_NAT(ofpacts);
++    nat->range_af = AF_UNSPEC;
++    nat->flags = 0;
++
++    ct = ofpbuf_at_assert(ofpacts, ct_offset, sizeof *ct);
++    ofpacts->header = ct;
++    ofpact_finish_CT(ofpacts, &ct);
+ }
+ 
+ static void
+ ovnact_ct_nat_free(struct ovnact_ct_nat *ct_nat OVS_UNUSED)
+ {
+ }
++
++static void
++ovnact_ct_commit_nat_free(struct ovnact_ct_commit_nat *cn OVS_UNUSED)
++{
++}
+ 
+ static void
+ parse_ct_lb_action(struct action_context *ctx, bool ct_lb_mark)
+@@ -5004,6 +5055,7 @@ encode_COMMIT_LB_AFF(const struct ovnact_commit_lb_aff *lb_aff,
      ol->hard_timeout = OFP_FLOW_PERMANENT;
      ol->priority = OFP_DEFAULT_PRIORITY;
      ol->table_id = OFTABLE_CHK_LB_AFFINITY;
@@ -3055,7 +3886,7 @@ index b43a146b4..90e6e470d 100644
  void ovn_extend_table_destroy(struct ovn_extend_table *);
  
 diff --git a/lib/features.c b/lib/features.c
-index d24e8f6c5..b31b5f6dd 100644
+index d24e8f6c5..d391930c6 100644
 --- a/lib/features.c
 +++ b/lib/features.c
 @@ -27,6 +27,7 @@
@@ -3085,7 +3916,14 @@ index d24e8f6c5..b31b5f6dd 100644
  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
  
  /* ovs-vswitchd connection. */
-@@ -145,11 +158,19 @@ ovs_feature_get_openflow_cap(const char *br_name)
+@@ -140,16 +153,26 @@ ovs_feature_get_openflow_cap(const char *br_name)
+ 
+     rconn_run(swconn);
+     if (!rconn_is_connected(swconn)) {
++        rconn_run_wait(swconn);
++        rconn_recv_wait(swconn);
+         return false;
+     }
  
      /* send new requests just after reconnect. */
      if (conn_seq_no != rconn_get_connection_seqno(swconn)) {
@@ -3106,7 +3944,7 @@ index d24e8f6c5..b31b5f6dd 100644
  
      bool ret = false;
      for (int i = 0; i < 50; i++) {
-@@ -163,21 +184,13 @@ ovs_feature_get_openflow_cap(const char *br_name)
+@@ -163,21 +186,13 @@ ovs_feature_get_openflow_cap(const char *br_name)
          ofptype_decode(&type, oh);
  
          if (type == OFPTYPE_METER_FEATURES_STATS_REPLY) {
@@ -3135,7 +3973,7 @@ index d24e8f6c5..b31b5f6dd 100644
          } else if (type == OFPTYPE_ECHO_REQUEST) {
              rconn_send(swconn, ofputil_encode_echo_reply(oh), NULL);
          }
-@@ -186,6 +199,26 @@ ovs_feature_get_openflow_cap(const char *br_name)
+@@ -186,6 +201,26 @@ ovs_feature_get_openflow_cap(const char *br_name)
      rconn_run_wait(swconn);
      rconn_recv_wait(swconn);
  
@@ -3162,7 +4000,7 @@ index d24e8f6c5..b31b5f6dd 100644
      return ret;
  }
  
-@@ -229,3 +262,26 @@ ovs_feature_support_run(const struct smap *ovs_capabilities,
+@@ -229,3 +264,26 @@ ovs_feature_support_run(const struct smap *ovs_capabilities,
      }
      return updated;
  }
@@ -3190,17 +4028,23 @@ index d24e8f6c5..b31b5f6dd 100644
 +    return ovs_group_features.max_groups[OFPGT11_SELECT];
 +}
 diff --git a/lib/logical-fields.c b/lib/logical-fields.c
-index fd509d9ee..7a1e66d0a 100644
+index fd509d9ee..20219a67a 100644
 --- a/lib/logical-fields.c
 +++ b/lib/logical-fields.c
-@@ -129,6 +129,10 @@ ovn_init_symtab(struct shash *symtab)
+@@ -129,6 +129,16 @@ ovn_init_symtab(struct shash *symtab)
               MLF_USE_SNAT_ZONE);
      expr_symtab_add_subfield(symtab, "flags.use_snat_zone", NULL,
                               flags_str);
 +    snprintf(flags_str, sizeof flags_str, "flags[%d]",
 +             MLF_LOCALNET_BIT);
 +    expr_symtab_add_subfield(symtab, "flags.localnet", NULL,
++                            flags_str);
++    snprintf(flags_str, sizeof flags_str, "flags[%d]",
++             MLF_ICMP_SNAT_BIT);
++    expr_symtab_add_subfield(symtab, "flags.icmp_snat", NULL,
 +                             flags_str);
++    snprintf(flags_str, sizeof flags_str, "flags[%d]", MLF_RX_FROM_TUNNEL_BIT);
++    expr_symtab_add_subfield(symtab, "flags.tunnel_rx", NULL, flags_str);
  
      /* Connection tracking state. */
      expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK, NULL, false,
@@ -3380,6 +4224,26 @@ index 250cec848..d06f46a54 100644
              /* Determine the lbs which are added or deleted for this
               * lb group and add them to tracked data.
               * Eg.  If an lb group lbg1 before the update had [lb1, lb2, lb3]
+diff --git a/northd/en-meters.c b/northd/en-meters.c
+index aabd002b6..793a46335 100644
+--- a/northd/en-meters.c
++++ b/northd/en-meters.c
+@@ -203,9 +203,13 @@ sync_acl_fair_meter(struct ovsdb_idl_txn *ovnsb_txn,
+                     const struct nbrec_acl *acl, struct shash *sb_meters,
+                     struct sset *used_sb_meters)
+ {
+-    const struct nbrec_meter *nb_meter =
+-        fair_meter_lookup_by_name(meter_groups, acl->meter);
++    const struct nbrec_meter *nb_meter;
++
++    if (!acl->log || !acl->meter) {
++        return;
++    }
+ 
++    nb_meter = fair_meter_lookup_by_name(meter_groups, acl->meter);
+     if (!nb_meter) {
+         return;
+     }
 diff --git a/northd/en-sync-sb.c b/northd/en-sync-sb.c
 index aae396a43..2ec3bf54f 100644
 --- a/northd/en-sync-sb.c
@@ -3446,10 +4310,10 @@ index 8b0817117..04df0b06c 100644
      engine_add_input(&en_sync_to_sb_pb, &en_northd,
                       sync_to_sb_pb_northd_handler);
 diff --git a/northd/northd.c b/northd/northd.c
-index 04afe62a8..8aeca33f8 100644
+index 04afe62a8..b1546f7bf 100644
 --- a/northd/northd.c
 +++ b/northd/northd.c
-@@ -491,6 +491,15 @@ build_chassis_features(const struct sbrec_chassis_table *sbrec_chassis_table,
+@@ -491,6 +491,24 @@ build_chassis_features(const struct sbrec_chassis_table *sbrec_chassis_table,
              chassis_features->fdb_timestamp) {
              chassis_features->fdb_timestamp = false;
          }
@@ -3462,10 +4326,19 @@ index 04afe62a8..8aeca33f8 100644
 +            chassis_features->ls_dpg_column) {
 +            chassis_features->ls_dpg_column = false;
 +        }
++
++        bool ct_commit_nat_v2 =
++                smap_get_bool(&chassis->other_config,
++                              OVN_FEATURE_CT_COMMIT_NAT_V2,
++                              false);
++        if (!ct_commit_nat_v2 &&
++            chassis_features->ct_commit_nat_v2) {
++            chassis_features->ct_commit_nat_v2 = false;
++        }
      }
  }
  
-@@ -4511,6 +4520,7 @@ struct sb_lb {
+@@ -4511,6 +4529,7 @@ struct sb_lb {
  
      const struct sbrec_load_balancer *slb;
      struct ovn_dp_group *dpg;
@@ -3473,7 +4346,7 @@ index 04afe62a8..8aeca33f8 100644
      struct uuid lb_uuid;
  };
  
-@@ -4533,10 +4543,13 @@ find_slb_in_sb_lbs(struct hmap *sb_lbs, const struct uuid *lb_uuid)
+@@ -4533,10 +4552,13 @@ find_slb_in_sb_lbs(struct hmap *sb_lbs, const struct uuid *lb_uuid)
  void
  sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
           const struct sbrec_load_balancer_table *sbrec_load_balancer_table,
@@ -3490,7 +4363,7 @@ index 04afe62a8..8aeca33f8 100644
      struct ovn_lb_datapaths *lb_dps;
      struct hmap sb_lbs = HMAP_INITIALIZER(&sb_lbs);
  
-@@ -4554,7 +4567,8 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
+@@ -4554,7 +4576,8 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
          }
  
          /* Delete any SB load balancer entries that refer to NB load balancers
@@ -3500,7 +4373,7 @@ index 04afe62a8..8aeca33f8 100644
           *
           * There is also a special case in which duplicate LBs might be created
           * in the SB, e.g., due to the fact that OVSDB only ensures
-@@ -4562,7 +4576,8 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
+@@ -4562,7 +4585,8 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
           * are not indexed in any way.
           */
          lb_dps = ovn_lb_datapaths_find(lb_dps_map, &lb_uuid);
@@ -3510,7 +4383,7 @@ index 04afe62a8..8aeca33f8 100644
              sbrec_load_balancer_delete(sbrec_lb);
              continue;
          }
-@@ -4573,12 +4588,26 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
+@@ -4573,12 +4597,26 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
          hmap_insert(&sb_lbs, &sb_lb->hmap_node, uuid_hash(&lb_uuid));
  
          /* Find or create datapath group for this load balancer. */
@@ -3543,7 +4416,7 @@ index 04afe62a8..8aeca33f8 100644
      }
      hmapx_destroy(&existing_lbs);
  
-@@ -4586,7 +4615,7 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
+@@ -4586,7 +4624,7 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
       * the SB load balancer columns. */
      HMAP_FOR_EACH (lb_dps, hmap_node, lb_dps_map) {
  
@@ -3552,7 +4425,7 @@ index 04afe62a8..8aeca33f8 100644
              continue;
          }
  
-@@ -4599,8 +4628,8 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
+@@ -4599,8 +4637,8 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
  
          struct sb_lb *sb_lb = find_slb_in_sb_lbs(&sb_lbs,
                                              &lb_dps->lb->nlb->header_.uuid);
@@ -3563,7 +4436,7 @@ index 04afe62a8..8aeca33f8 100644
          if (!sb_lb) {
              sbrec_lb = sbrec_load_balancer_insert(ovnsb_txn);
              char *lb_id = xasprintf(
-@@ -4612,15 +4641,29 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
+@@ -4612,15 +4650,29 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
          } else {
              sbrec_lb = sb_lb->slb;
              lb_dpg = sb_lb->dpg;
@@ -3599,7 +4472,7 @@ index 04afe62a8..8aeca33f8 100644
          }
  
          /* Update columns. */
-@@ -4628,7 +4671,23 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
+@@ -4628,7 +4680,23 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
          sbrec_load_balancer_set_vips(sbrec_lb,
                                       ovn_northd_lb_get_vips(lb_dps->lb));
          sbrec_load_balancer_set_protocol(sbrec_lb, lb_dps->lb->nlb->protocol);
@@ -3624,39 +4497,43 @@ index 04afe62a8..8aeca33f8 100644
          sbrec_load_balancer_set_options(sbrec_lb, &options);
          /* Clearing 'datapaths' column, since 'dp_group' is in use. */
          sbrec_load_balancer_set_datapaths(sbrec_lb, NULL, 0);
-@@ -4636,11 +4695,17 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
+@@ -4636,11 +4704,17 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
      }
  
      struct ovn_dp_group *dpg;
 -    HMAP_FOR_EACH_POP (dpg, node, &dp_groups) {
 +    HMAP_FOR_EACH_POP (dpg, node, &ls_dp_groups) {
-+        bitmap_free(dpg->bitmap);
-+        free(dpg);
-+    }
-+    hmap_destroy(&ls_dp_groups);
-+
-+    HMAP_FOR_EACH_POP (dpg, node, &lr_dp_groups) {
          bitmap_free(dpg->bitmap);
          free(dpg);
      }
 -    hmap_destroy(&dp_groups);
++    hmap_destroy(&ls_dp_groups);
++
++    HMAP_FOR_EACH_POP (dpg, node, &lr_dp_groups) {
++        bitmap_free(dpg->bitmap);
++        free(dpg);
++    }
 +    hmap_destroy(&lr_dp_groups);
  
      struct sb_lb *sb_lb;
      HMAP_FOR_EACH_POP (sb_lb, hmap_node, &sb_lbs) {
-@@ -4659,6 +4724,33 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
-             sbrec_datapath_binding_set_load_balancers(od->sb, NULL, 0);
-         }
-     }
-+    HMAP_FOR_EACH (od, key_node, &lr_datapaths->datapaths) {
-+        ovs_assert(od->nbr);
-+
+@@ -4655,12 +4729,39 @@ sync_lbs(struct ovsdb_idl_txn *ovnsb_txn,
+     HMAP_FOR_EACH (od, key_node, &ls_datapaths->datapaths) {
+         ovs_assert(od->nbs);
+ 
 +        if (od->sb->n_load_balancers) {
 +            sbrec_datapath_binding_set_load_balancers(od->sb, NULL, 0);
 +        }
 +    }
-+}
++    HMAP_FOR_EACH (od, key_node, &lr_datapaths->datapaths) {
++        ovs_assert(od->nbr);
 +
+         if (od->sb->n_load_balancers) {
+             sbrec_datapath_binding_set_load_balancers(od->sb, NULL, 0);
+         }
+     }
+ }
+ 
 +bool
 +check_sb_lb_duplicates(const struct sbrec_load_balancer_table *table)
 +{
@@ -3675,10 +4552,12 @@ index 04afe62a8..8aeca33f8 100644
 +
 +    sset_destroy(&existing_nb_lb_uuids);
 +    return duplicates;
- }
- 
++}
++
  /* Syncs the SB port binding for the ovn_port 'op'.  Caller should make sure
-@@ -5089,23 +5181,21 @@ lsp_can_be_inc_processed(const struct nbrec_logical_switch_port *nbsp)
+  * that the OVN SB IDL txn is not NULL.  Presently it only syncs the nat
+  * column of port binding corresponding to the 'op->nbsp' */
+@@ -5089,23 +5190,21 @@ lsp_can_be_inc_processed(const struct nbrec_logical_switch_port *nbsp)
  }
  
  static bool
@@ -3708,7 +4587,7 @@ index 04afe62a8..8aeca33f8 100644
              return op;
          }
      }
-@@ -5290,7 +5380,7 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
+@@ -5290,7 +5389,7 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
      /* Compare the individual ports in the old and new Logical Switches */
      for (size_t j = 0; j < changed_ls->n_ports; ++j) {
          struct nbrec_logical_switch_port *new_nbsp = changed_ls->ports[j];
@@ -3717,7 +4596,7 @@ index 04afe62a8..8aeca33f8 100644
  
          if (!op) {
              if (!lsp_can_be_inc_processed(new_nbsp)) {
-@@ -5307,7 +5397,7 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
+@@ -5307,7 +5406,7 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn *ovnsb_idl_txn,
              }
              ovs_list_push_back(&ls_change->added_ports,
                                  &op->list);
@@ -3726,7 +4605,7 @@ index 04afe62a8..8aeca33f8 100644
              /* Existing port updated */
              bool temp = false;
              if (lsp_is_type_changed(op->sb, new_nbsp, &temp) ||
-@@ -5580,9 +5670,9 @@ northd_handle_sb_port_binding_changes(
+@@ -5580,9 +5679,9 @@ northd_handle_sb_port_binding_changes(
               * notification of that transaction, and we can ignore in this
               * case. Fallback to recompute otherwise, to avoid dangling
               * sb idl pointers and other unexpected behavior. */
@@ -3739,7 +4618,7 @@ index 04afe62a8..8aeca33f8 100644
                  return false;
              }
          } else {
-@@ -6954,6 +7044,9 @@ build_lswitch_learn_fdb_op(
+@@ -6954,6 +7053,9 @@ build_lswitch_learn_fdb_op(
          ds_clear(match);
          ds_clear(actions);
          ds_put_format(match, "inport == %s", op->json_key);
@@ -3749,7 +4628,58 @@ index 04afe62a8..8aeca33f8 100644
          ds_put_format(actions, REGBIT_LKUP_FDB
                        " = lookup_fdb(inport, eth.src); next;");
          ovn_lflow_add_with_lport_and_hint(lflows, op->od,
-@@ -9307,6 +9400,37 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
+@@ -8291,12 +8393,12 @@ build_lb_rules_pre_stateful(struct hmap *lflows,
+  *
+  * - load balancing:
+  *   table=lr_in_dnat, priority=150
+- *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4
++ *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4.dst == V
+  *             && REG_LB_AFF_BACKEND_IP4 == B1 && REG_LB_AFF_MATCH_PORT == BP1)
+  *      action=(REG_NEXT_HOP_IPV4 = V; lb_action;
+  *              ct_lb_mark(backends=B1:BP1; ct_flag);)
+  *   table=lr_in_dnat, priority=150
+- *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4
++ *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4.dst == V
+  *             && REG_LB_AFF_BACKEND_IP4 == B2 && REG_LB_AFF_MATCH_PORT == BP2)
+  *      action=(REG_NEXT_HOP_IPV4 = V; lb_action;
+  *              ct_lb_mark(backends=B2:BP2; ct_flag);)
+@@ -8395,7 +8497,8 @@ build_lb_affinity_lr_flows(struct hmap *lflows, const struct ovn_northd_lb *lb,
+ 
+     /* Prepare common part of affinity match. */
+     ds_put_format(&aff_match, REGBIT_KNOWN_LB_SESSION" == 1 && "
+-                  "ct.new && %s && %s == ", ip_match, reg_backend);
++                  "ct.new && %s.dst == %s && %s == ", ip_match,
++                  lb_vip->vip_str, reg_backend);
+ 
+     /* Store the common part length. */
+     size_t aff_action_len = aff_action.length;
+@@ -8474,13 +8577,13 @@ build_lb_affinity_lr_flows(struct hmap *lflows, const struct ovn_northd_lb *lb,
+  *
+  * - load balancing:
+  *   table=ls_in_lb, priority=150
+- *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4
++ *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4.dst == V
+  *             && REG_LB_AFF_BACKEND_IP4 == B1 && REG_LB_AFF_MATCH_PORT == BP1)
+  *      action=(REGBIT_CONNTRACK_COMMIT = 0;
+  *              REG_ORIG_DIP_IPV4 = V; REG_ORIG_TP_DPORT = VP;
+  *              ct_lb_mark(backends=B1:BP1);)
+  *   table=ls_in_lb, priority=150
+- *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4
++ *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4.dst == V
+  *             && REG_LB_AFF_BACKEND_IP4 == B2 && REG_LB_AFF_MATCH_PORT == BP2)
+  *      action=(REGBIT_CONNTRACK_COMMIT = 0;
+  *              REG_ORIG_DIP_IPV4 = V;
+@@ -8583,7 +8686,8 @@ build_lb_affinity_ls_flows(struct hmap *lflows,
+ 
+     /* Prepare common part of affinity match. */
+     ds_put_format(&aff_match, REGBIT_KNOWN_LB_SESSION" == 1 && "
+-                  "ct.new && %s && %s == ", ip_match, reg_backend);
++                  "ct.new && %s.dst == %s && %s == ", ip_match,
++                  lb_vip->vip_str, reg_backend);
+ 
+     /* Store the common part length. */
+     size_t aff_action_len = aff_action.length;
+@@ -9307,6 +9411,37 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
          }
      }
  
@@ -3787,7 +4717,270 @@ index 04afe62a8..8aeca33f8 100644
      for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
          build_lswitch_rport_arp_req_flow(
              op->lrp_networks.ipv4_addrs[i].addr_s, AF_INET, sw_op, sw_od, 80,
-@@ -13047,9 +13171,9 @@ build_neigh_learning_flows_for_lrouter(
+@@ -9651,6 +9786,13 @@ build_lswitch_lflows_admission_control(struct ovn_datapath *od,
+                                        struct hmap *lflows)
+ {
+     ovs_assert(od->nbs);
++
++    /* Default action for recirculated ICMP error 'packet too big'. */
++    ovn_lflow_add(lflows, od, S_SWITCH_IN_CHECK_PORT_SEC, 110,
++                  "((ip4 && icmp4.type == 3 && icmp4.code == 4) ||"
++                  " (ip6 && icmp6.type == 2 && icmp6.code == 0)) &&"
++                  " flags.tunnel_rx == 1", debug_drop_action());
++
+     /* Logical VLANs not supported. */
+     if (!is_vlan_transparent(od)) {
+         /* Block logical VLANs. */
+@@ -11385,7 +11527,6 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
+                                struct ds *route_match)
+ {
+     const struct nbrec_logical_router_static_route *st_route = route->route;
+-    struct ds base_match = DS_EMPTY_INITIALIZER;
+     struct ds match = DS_EMPTY_INITIALIZER;
+     struct ds actions = DS_EMPTY_INITIALIZER;
+     struct ds ecmp_reply = DS_EMPTY_INITIALIZER;
+@@ -11397,14 +11538,14 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
+     /* If symmetric ECMP replies are enabled, then packets that arrive over
+      * an ECMP route need to go through conntrack.
+      */
+-    ds_put_format(&base_match, "inport == %s && ip%s.%s == %s",
++    ds_put_format(&match, "inport == %s && ip%s.%s == %s",
+                   out_port->json_key,
+                   IN6_IS_ADDR_V4MAPPED(&route->prefix) ? "4" : "6",
+                   route->is_src_route ? "dst" : "src",
+                   cidr);
+     free(cidr);
+     ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DEFRAG, 100,
+-                             ds_cstr(&base_match), "ct_next;",
++                             ds_cstr(&match), "ct_next;",
+                              &st_route->header_);
+ 
+     /* And packets that go out over an ECMP route need conntrack */
+@@ -11418,73 +11559,7 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
+      * NOTE: we purposely are not clearing match before this
+      * ds_put_cstr() call. The previous contents are needed.
+      */
+-    ds_put_format(&match, "%s && (ct.new && !ct.est) && tcp",
+-                  ds_cstr(&base_match));
+-    ds_put_format(&actions,
+-            "ct_commit { ct_label.ecmp_reply_eth = eth.src; "
+-            " %s = %" PRId64 ";}; "
+-            "next;",
+-            ct_ecmp_reply_port_match, out_port->sb->tunnel_key);
+-    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100,
+-                            ds_cstr(&match), ds_cstr(&actions),
+-                            &st_route->header_);
+-    ds_clear(&match);
+-    ds_put_format(&match, "%s && (ct.new && !ct.est) && udp",
+-                  ds_cstr(&base_match));
+-    ds_clear(&actions);
+-    ds_put_format(&actions,
+-            "ct_commit { ct_label.ecmp_reply_eth = eth.src; "
+-            " %s = %" PRId64 ";}; "
+-            "next;",
+-            ct_ecmp_reply_port_match, out_port->sb->tunnel_key);
+-    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100,
+-                            ds_cstr(&match), ds_cstr(&actions),
+-                            &st_route->header_);
+-    ds_clear(&match);
+-    ds_put_format(&match, "%s && (ct.new && !ct.est) && sctp",
+-                  ds_cstr(&base_match));
+-    ds_clear(&actions);
+-    ds_put_format(&actions,
+-            "ct_commit { ct_label.ecmp_reply_eth = eth.src; "
+-            " %s = %" PRId64 ";}; "
+-            "next;",
+-            ct_ecmp_reply_port_match, out_port->sb->tunnel_key);
+-    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100,
+-                            ds_cstr(&match), ds_cstr(&actions),
+-                            &st_route->header_);
+-
+-    ds_clear(&match);
+-    ds_put_format(&match,
+-            "%s && (!ct.rpl && ct.est) && tcp",
+-            ds_cstr(&base_match));
+-    ds_clear(&actions);
+-    ds_put_format(&actions,
+-            "ct_commit { ct_label.ecmp_reply_eth = eth.src; "
+-            " %s = %" PRId64 ";}; "
+-            "next;",
+-            ct_ecmp_reply_port_match, out_port->sb->tunnel_key);
+-    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100,
+-                            ds_cstr(&match), ds_cstr(&actions),
+-                            &st_route->header_);
+-
+-    ds_clear(&match);
+-    ds_put_format(&match,
+-            "%s && (!ct.rpl && ct.est) && udp",
+-            ds_cstr(&base_match));
+-    ds_clear(&actions);
+-    ds_put_format(&actions,
+-            "ct_commit { ct_label.ecmp_reply_eth = eth.src; "
+-            " %s = %" PRId64 ";}; "
+-            "next;",
+-            ct_ecmp_reply_port_match, out_port->sb->tunnel_key);
+-    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 100,
+-                            ds_cstr(&match), ds_cstr(&actions),
+-                            &st_route->header_);
+-    ds_clear(&match);
+-    ds_put_format(&match,
+-            "%s && (!ct.rpl && ct.est) && sctp",
+-            ds_cstr(&base_match));
+-    ds_clear(&actions);
++    ds_put_cstr(&match, " && !ct.rpl && (ct.new || ct.est)");
+     ds_put_format(&actions,
+             "ct_commit { ct_label.ecmp_reply_eth = eth.src; "
+             " %s = %" PRId64 ";}; "
+@@ -11534,7 +11609,6 @@ add_ecmp_symmetric_reply_flows(struct hmap *lflows,
+                             200, ds_cstr(&ecmp_reply),
+                             action, &st_route->header_);
+ 
+-    ds_destroy(&base_match);
+     ds_destroy(&match);
+     ds_destroy(&actions);
+     ds_destroy(&ecmp_reply);
+@@ -11950,7 +12024,6 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip,
+     struct ds skip_snat_act = DS_EMPTY_INITIALIZER;
+     struct ds force_snat_act = DS_EMPTY_INITIALIZER;
+     struct ds undnat_match = DS_EMPTY_INITIALIZER;
+-    struct ds unsnat_match = DS_EMPTY_INITIALIZER;
+     struct ds gw_redir_action = DS_EMPTY_INITIALIZER;
+ 
+     ds_clear(match);
+@@ -11996,13 +12069,6 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip,
+     /* Remove the trailing " || ". */
+     ds_truncate(&undnat_match, undnat_match.length - 4);
+ 
+-    ds_put_format(&unsnat_match, "%s && %s.dst == %s && %s",
+-                  ip_match, ip_match, lb_vip->vip_str, lb->proto);
+-    if (lb_vip->port_str) {
+-        ds_put_format(&unsnat_match, " && %s.dst == %s", lb->proto,
+-                      lb_vip->port_str);
+-    }
+-
+     struct lrouter_nat_lb_flows_ctx ctx = {
+         .lb_vip = lb_vip,
+         .lb = lb,
+@@ -12054,23 +12120,6 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip,
+         if (lb->affinity_timeout) {
+             bitmap_set1(aff_dp_bitmap[type], index);
+         }
+-
+-        if (sset_contains(&od->external_ips, lb_vip->vip_str)) {
+-            /* The load balancer vip is also present in the NAT entries.
+-             * So add a high priority lflow to advance the the packet
+-             * destined to the vip (and the vip port if defined)
+-             * in the S_ROUTER_IN_UNSNAT stage.
+-             * There seems to be an issue with ovs-vswitchd. When the new
+-             * connection packet destined for the lb vip is received,
+-             * it is dnat'ed in the S_ROUTER_IN_DNAT stage in the dnat
+-             * conntrack zone. For the next packet, if it goes through
+-             * unsnat stage, the conntrack flags are not set properly, and
+-             * it doesn't hit the established state flows in
+-             * S_ROUTER_IN_DNAT stage. */
+-            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_UNSNAT, 120,
+-                                    ds_cstr(&unsnat_match), "next;",
+-                                    &lb->nlb->header_);
+-        }
+     }
+ 
+     for (size_t type = 0; type < LROUTER_NAT_LB_FLOW_MAX; type++) {
+@@ -12081,7 +12130,6 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip,
+                                    lr_datapaths);
+     }
+ 
+-    ds_destroy(&unsnat_match);
+     ds_destroy(&undnat_match);
+     ds_destroy(&skip_snat_act);
+     ds_destroy(&force_snat_act);
+@@ -12690,6 +12738,72 @@ build_lrouter_force_snat_flows(struct hmap *lflows, struct ovn_datapath *od,
+     ds_destroy(&actions);
+ }
+ 
++/* Following flows are used to manage traffic redirected by the kernel
++ * (e.g. ICMP errors packets) that enter the cluster from the geneve ports
++ */
++static void
++build_lrouter_icmp_packet_toobig_admin_flows(
++        struct ovn_port *op, struct hmap *lflows,
++        struct ds *match, struct ds *actions)
++{
++    ovs_assert(op->nbrp);
++
++    if (!is_l3dgw_port(op)) {
++        return;
++    }
++
++    ds_clear(match);
++    ds_put_format(match,
++                  "((ip4 && icmp4.type == 3 && icmp4.code == 4) ||"
++                  " (ip6 && icmp6.type == 2 && icmp6.code == 0)) &&"
++                  " eth.dst == %s && !is_chassis_resident(%s) &&"
++                  " flags.tunnel_rx == 1",
++                  op->nbrp->mac, op->cr_port->json_key);
++    ds_clear(actions);
++    ds_put_format(actions, "outport <-> inport; inport = %s; next;",
++                  op->json_key);
++    ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 120,
++                  ds_cstr(match), ds_cstr(actions));
++}
++
++static void
++build_lswitch_icmp_packet_toobig_admin_flows(
++        struct ovn_port *op, struct hmap *lflows,
++        struct ds *match, struct ds *actions)
++{
++    ovs_assert(op->nbsp);
++
++    if (!lsp_is_router(op->nbsp)) {
++        return;
++    }
++
++    struct ovn_port *peer = op->peer;
++    if (!peer) {
++        return;
++    }
++
++    ds_clear(match);
++    if (peer->od->is_gw_router) {
++        ds_put_format(match,
++                      "((ip4 && icmp4.type == 3 && icmp4.code == 4) ||"
++                      " (ip6 && icmp6.type == 2 && icmp6.code == 0)) && "
++                      "eth.src == %s && outport == %s && flags.tunnel_rx == 1",
++                      peer->nbrp->mac, op->json_key);
++    } else {
++        ds_put_format(match,
++                      "((ip4 && icmp4.type == 3 && icmp4.code == 4) ||"
++                      " (ip6 && icmp6.type == 2 && icmp6.code == 0)) && "
++                      "eth.dst == %s && flags.tunnel_rx == 1",
++                      peer->nbrp->mac);
++    }
++    ds_clear(actions);
++    ds_put_format(actions,
++                  "outport <-> inport; next(pipeline=ingress,table=%d);",
++                  ovn_stage_get_table(S_SWITCH_IN_L2_LKUP));
++    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_CHECK_PORT_SEC, 120,
++                  ds_cstr(match), ds_cstr(actions));
++}
++
+ static void
+ build_lrouter_force_snat_flows_op(struct ovn_port *op,
+                                   struct hmap *lflows,
+@@ -12822,6 +12936,13 @@ build_adm_ctrl_flows_for_lrouter(
+         struct ovn_datapath *od, struct hmap *lflows)
+ {
+     ovs_assert(od->nbr);
++
++    /* Default action for recirculated ICMP error 'packet too big'. */
++    ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 110,
++                  "((ip4 && icmp4.type == 3 && icmp4.code == 4) ||"
++                  " (ip6 && icmp6.type == 2 && icmp6.code == 0)) &&"
++                  " flags.tunnel_rx == 1", debug_drop_action());
++
+     /* Logical VLANs not supported.
+      * Broadcast/multicast source address is invalid. */
+     ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 100,
+@@ -13047,9 +13168,9 @@ build_neigh_learning_flows_for_lrouter(
           *   address, the all-nodes multicast address. */
          ds_clear(actions);
          ds_put_format(actions, REGBIT_LOOKUP_NEIGHBOR_RESULT
@@ -3799,65 +4992,220 @@ index 04afe62a8..8aeca33f8 100644
          ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 110,
                        "nd_na && ip6.src == fe80::/10 && ip6.dst == ff00::/8",
                        ds_cstr(actions));
-@@ -13895,7 +14019,15 @@ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
-                                               outport->json_key)
-                                   : NULL;
+@@ -13885,82 +14006,103 @@ build_arp_resolve_flows_for_lsp(
+     }
+ }
  
--    if (op->lrp_networks.ipv4_addrs) {
-+    char *ip4_src = NULL;
++#define ICMP4_NEED_FRAG_FORMAT                           \
++    "icmp4_error {"                                      \
++    "%s"                                                 \
++    REGBIT_EGRESS_LOOPBACK" = 1; "                       \
++    REGBIT_PKT_LARGER" = 0; "                            \
++    "eth.dst = %s; "                                     \
++    "ip4.dst = ip4.src; "                                \
++    "ip4.src = %s; "                                     \
++    "ip.ttl = 255; "                                     \
++    "icmp4.type = 3; /* Destination Unreachable. */ "    \
++    "icmp4.code = 4; /* Frag Needed and DF was Set. */ " \
++    "icmp4.frag_mtu = %d; "                              \
++    "next(pipeline=ingress, table=%d); };"               \
++
++#define ICMP6_NEED_FRAG_FORMAT               \
++    "icmp6_error {"                          \
++    "%s"                                     \
++    REGBIT_EGRESS_LOOPBACK" = 1; "           \
++    REGBIT_PKT_LARGER" = 0; "                \
++    "eth.dst = %s; "                         \
++    "ip6.dst = ip6.src; "                    \
++    "ip6.src = %s; "                         \
++    "ip.ttl = 255; "                         \
++    "icmp6.type = 2; /* Packet Too Big. */ " \
++    "icmp6.code = 0; "                       \
++    "icmp6.frag_mtu = %d; "                  \
++    "next(pipeline=ingress, table=%d); };"
++
++static void
++create_icmp_need_frag_lflow(const struct ovn_port *op, int mtu,
++                            struct ds *actions, struct ds *match,
++                            const char *meter, struct hmap *lflows,
++                            enum ovn_stage stage, uint16_t priority,
++                            bool is_ipv6, const char *extra_match,
++                            const char *extra_action)
++{
++    if ((is_ipv6 && !op->lrp_networks.ipv6_addrs) ||
++        (!is_ipv6 && !op->lrp_networks.ipv4_addrs)) {
++        return;
++    }
++
++    const char *ip = is_ipv6
++                     ? op->lrp_networks.ipv6_addrs[0].addr_s
++                     : op->lrp_networks.ipv4_addrs[0].addr_s;
++    size_t match_len = match->length;
 +
-+    if (outport && outport->lrp_networks.ipv4_addrs) {
-+        ip4_src = outport->lrp_networks.ipv4_addrs[0].addr_s;
-+    } else if (op->lrp_networks.ipv4_addrs) {
-+        ip4_src = op->lrp_networks.ipv4_addrs[0].addr_s;
++    ds_put_format(match, " && ip%c && "REGBIT_PKT_LARGER
++                  " && "REGBIT_EGRESS_LOOPBACK" == 0", is_ipv6 ? '6' : '4');
++
++    if (*extra_match) {
++        ds_put_format(match, " && %s", extra_match);
 +    }
 +
-+    if (ip4_src) {
-         ds_clear(match);
-         ds_put_format(match, "inport == %s && %sip4 && "REGBIT_PKT_LARGER
-                       " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
-@@ -13915,9 +14047,8 @@ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
-             "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
-             "icmp4.frag_mtu = %d; "
-             "next(pipeline=ingress, table=%d); };",
++    ds_clear(actions);
++    ds_put_format(actions,
++                  is_ipv6 ? ICMP6_NEED_FRAG_FORMAT : ICMP4_NEED_FRAG_FORMAT,
++                  extra_action, op->lrp_networks.ea_s, ip,
++                  mtu, ovn_stage_get_table(S_ROUTER_IN_ADMISSION));
++
++    ovn_lflow_add_with_hint__(lflows, op->od, stage, priority,
++                              ds_cstr(match), ds_cstr(actions),
++                              NULL, meter, &op->nbrp->header_);
++
++    ds_truncate(match, match_len);
++}
++
+ static void
+ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
+                             const struct shash *meter_groups, struct ds *match,
+                             struct ds *actions, enum ovn_stage stage,
+                             struct ovn_port *outport)
+ {
+-    char *outport_match = outport ? xasprintf("outport == %s && ",
+-                                              outport->json_key)
+-                                  : NULL;
+-
+-    if (op->lrp_networks.ipv4_addrs) {
+-        ds_clear(match);
+-        ds_put_format(match, "inport == %s && %sip4 && "REGBIT_PKT_LARGER
+-                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
+-                      outport ? outport_match : "");
++    const char *ipv4_meter = copp_meter_get(COPP_ICMP4_ERR, op->od->nbr->copp,
++                                            meter_groups);
++    const char *ipv6_meter = copp_meter_get(COPP_ICMP6_ERR, op->od->nbr->copp,
++                                            meter_groups);
+ 
+-        ds_clear(actions);
+-        /* Set icmp4.frag_mtu to gw_mtu */
+-        ds_put_format(actions,
+-            "icmp4_error {"
+-            REGBIT_EGRESS_LOOPBACK" = 1; "
+-            REGBIT_PKT_LARGER" = 0; "
+-            "eth.dst = %s; "
+-            "ip4.dst = ip4.src; "
+-            "ip4.src = %s; "
+-            "ip.ttl = 255; "
+-            "icmp4.type = 3; /* Destination Unreachable. */ "
+-            "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
+-            "icmp4.frag_mtu = %d; "
+-            "next(pipeline=ingress, table=%d); };",
 -            op->lrp_networks.ea_s,
 -            op->lrp_networks.ipv4_addrs[0].addr_s,
 -            mtu, ovn_stage_get_table(S_ROUTER_IN_ADMISSION));
-+            op->lrp_networks.ea_s, ip4_src, mtu,
-+            ovn_stage_get_table(S_ROUTER_IN_ADMISSION));
-         ovn_lflow_add_with_hint__(lflows, op->od, stage, 150,
-                                   ds_cstr(match), ds_cstr(actions),
-                                   NULL,
-@@ -13928,7 +14059,15 @@ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
-                                   &op->nbrp->header_);
-     }
+-        ovn_lflow_add_with_hint__(lflows, op->od, stage, 150,
+-                                  ds_cstr(match), ds_cstr(actions),
+-                                  NULL,
+-                                  copp_meter_get(
+-                                        COPP_ICMP4_ERR,
+-                                        op->od->nbr->copp,
+-                                        meter_groups),
+-                                  &op->nbrp->header_);
+-    }
++    ds_clear(match);
++    ds_put_format(match, "inport == %s", op->json_key);
  
 -    if (op->lrp_networks.ipv6_addrs) {
-+    char *ip6_src = NULL;
-+
-+    if (outport && outport->lrp_networks.ipv6_addrs) {
-+        ip6_src = outport->lrp_networks.ipv6_addrs[0].addr_s;
-+    } else if (op->lrp_networks.ipv6_addrs) {
-+        ip6_src = op->lrp_networks.ipv6_addrs[0].addr_s;
-+    }
-+
-+    if (ip6_src) {
-         ds_clear(match);
-         ds_put_format(match, "inport == %s && %sip6 && "REGBIT_PKT_LARGER
-                       " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
-@@ -13948,9 +14087,8 @@ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
-             "icmp6.code = 0; "
-             "icmp6.frag_mtu = %d; "
-             "next(pipeline=ingress, table=%d); };",
+-        ds_clear(match);
+-        ds_put_format(match, "inport == %s && %sip6 && "REGBIT_PKT_LARGER
+-                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
+-                      outport ? outport_match : "");
++    if (outport) {
++        ds_put_format(match, " && outport == %s", outport->json_key);
+ 
+-        ds_clear(actions);
+-        /* Set icmp6.frag_mtu to gw_mtu */
+-        ds_put_format(actions,
+-            "icmp6_error {"
+-            REGBIT_EGRESS_LOOPBACK" = 1; "
+-            REGBIT_PKT_LARGER" = 0; "
+-            "eth.dst = %s; "
+-            "ip6.dst = ip6.src; "
+-            "ip6.src = %s; "
+-            "ip.ttl = 255; "
+-            "icmp6.type = 2; /* Packet Too Big. */ "
+-            "icmp6.code = 0; "
+-            "icmp6.frag_mtu = %d; "
+-            "next(pipeline=ingress, table=%d); };",
 -            op->lrp_networks.ea_s,
 -            op->lrp_networks.ipv6_addrs[0].addr_s,
 -            mtu, ovn_stage_get_table(S_ROUTER_IN_ADMISSION));
-+            op->lrp_networks.ea_s, ip6_src, mtu,
-+            ovn_stage_get_table(S_ROUTER_IN_ADMISSION));
-         ovn_lflow_add_with_hint__(lflows, op->od, stage, 150,
-                                   ds_cstr(match), ds_cstr(actions),
-                                   NULL,
-@@ -15217,6 +15355,7 @@ build_lrouter_out_snat_in_czone_flow(struct hmap *lflows,
+-        ovn_lflow_add_with_hint__(lflows, op->od, stage, 150,
+-                                  ds_cstr(match), ds_cstr(actions),
+-                                  NULL,
+-                                  copp_meter_get(
+-                                        COPP_ICMP6_ERR,
+-                                        op->od->nbr->copp,
+-                                        meter_groups),
+-                                  &op->nbrp->header_);
++        create_icmp_need_frag_lflow(op, mtu, actions, match, ipv4_meter,
++                                    lflows, stage, 160, false,
++                                    "ct.trk && ct.rpl && ct.dnat",
++                                    "flags.icmp_snat = 1; ");
++        create_icmp_need_frag_lflow(op, mtu, actions, match, ipv6_meter,
++                                    lflows, stage, 160, true,
++                                    "ct.trk && ct.rpl && ct.dnat",
++                                    "flags.icmp_snat = 1; ");
+     }
+-    free(outport_match);
++
++    create_icmp_need_frag_lflow(op, mtu, actions, match, ipv4_meter, lflows,
++                                stage, 150, false, "", "");
++    create_icmp_need_frag_lflow(op, mtu, actions, match, ipv6_meter, lflows,
++                                stage, 150, true, "", "");
+ }
+ 
+ static void
+@@ -13968,8 +14110,8 @@ build_check_pkt_len_flows_for_lrp(struct ovn_port *op,
+                                   struct hmap *lflows,
+                                   const struct hmap *lr_ports,
+                                   const struct shash *meter_groups,
+-                                  struct ds *match,
+-                                  struct ds *actions)
++                                  struct ds *match, struct ds *actions,
++                                  const struct chassis_features *features)
+ {
+     int gw_mtu = smap_get_int(&op->nbrp->options, "gateway_mtu", 0);
+     if (gw_mtu <= 0) {
+@@ -13998,6 +14140,12 @@ build_check_pkt_len_flows_for_lrp(struct ovn_port *op,
+                                     match, actions, S_ROUTER_IN_LARGER_PKTS,
+                                     op);
+     }
++
++    if (features->ct_commit_nat_v2) {
++        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_OUT_POST_SNAT, 100,
++                                "icmp && flags.icmp_snat == 1",
++                                "ct_commit_nat(snat);", &op->nbrp->header_);
++    }
+ }
+ 
+ /* Local router ingress table CHK_PKT_LEN: Check packet length.
+@@ -14018,7 +14166,8 @@ build_check_pkt_len_flows_for_lrouter(
+         struct ovn_datapath *od, struct hmap *lflows,
+         const struct hmap *lr_ports,
+         struct ds *match, struct ds *actions,
+-        const struct shash *meter_groups)
++        const struct shash *meter_groups,
++        const struct chassis_features *features)
+ {
+     ovs_assert(od->nbr);
+ 
+@@ -14035,7 +14184,7 @@ build_check_pkt_len_flows_for_lrouter(
+             continue;
+         }
+         build_check_pkt_len_flows_for_lrp(rp, lflows, lr_ports, meter_groups,
+-                                          match, actions);
++                                          match, actions, features);
+     }
+ }
+ 
+@@ -15217,6 +15366,7 @@ build_lrouter_out_snat_in_czone_flow(struct hmap *lflows,
          ds_put_format(&zone_actions, "eth.src = "ETH_ADDR_FMT"; ",
                        ETH_ADDR_ARGS(mac));
      }
@@ -3865,7 +5213,7 @@ index 04afe62a8..8aeca33f8 100644
  
      ds_put_cstr(&zone_actions, REGBIT_DST_NAT_IP_LOCAL" = 0; ");
  
-@@ -15275,10 +15414,8 @@ build_lrouter_out_snat_flow(struct hmap *lflows, struct ovn_datapath *od,
+@@ -15275,10 +15425,8 @@ build_lrouter_out_snat_flow(struct hmap *lflows, struct ovn_datapath *od,
              ds_put_format(actions, "eth.src = "ETH_ADDR_FMT"; ",
                            ETH_ADDR_ARGS(mac));
          }
@@ -3877,15 +5225,56 @@ index 04afe62a8..8aeca33f8 100644
  
      ds_put_format(actions, "ct_snat(%s", nat->external_ip);
      if (nat->external_port_range[0]) {
-@@ -17655,6 +17792,7 @@ northd_init(struct northd_data *data)
+@@ -15704,12 +15852,7 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath *od, struct hmap *lflows,
+                                    l3dgw_port, stateless);
+ 
+         /* ARP resolve for NAT IPs. */
+-        if (od->is_gw_router) {
+-            /* Add the NAT external_ip to the nat_entries for
+-             * gateway routers. This is required for adding load balancer
+-             * flows.*/
+-            sset_add(&nat_entries, nat->external_ip);
+-        } else {
++        if (!od->is_gw_router) {
+             if (!sset_contains(&nat_entries, nat->external_ip)) {
+                 /* Drop packets coming in from external that still has
+                  * destination IP equals to the NAT external IP, to avoid loop.
+@@ -16006,7 +16149,7 @@ build_lswitch_and_lrouter_iterate_by_lr(struct ovn_datapath *od,
+     build_arp_resolve_flows_for_lrouter(od, lsi->lflows);
+     build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
+                                           &lsi->match, &lsi->actions,
+-                                          lsi->meter_groups);
++                                          lsi->meter_groups, lsi->features);
+     build_gateway_redirect_flows_for_lrouter(od, lsi->lflows, &lsi->match,
+                                              &lsi->actions);
+     build_arp_request_flows_for_lrouter(od, lsi->lflows, &lsi->match,
+@@ -16044,6 +16187,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op,
+     build_lswitch_dhcp_options_and_response(op, lflows, meter_groups);
+     build_lswitch_external_port(op, lflows);
+     build_lswitch_ip_unicast_lookup(op, lflows, actions, match);
++    build_lswitch_icmp_packet_toobig_admin_flows(op, lflows, match, actions);
+ 
+     /* Build Logical Router Flows. */
+     build_ip_routing_flows_for_router_type_lsp(op, lr_ports, lflows);
+@@ -16080,6 +16224,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op,
+                                 &lsi->match, &lsi->actions, lsi->meter_groups);
+     build_lrouter_force_snat_flows_op(op, lsi->lflows, &lsi->match,
+                                       &lsi->actions);
++    build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows, &lsi->match,
++                                                 &lsi->actions);
+ }
+ 
+ static void *
+@@ -17655,6 +17801,8 @@ northd_init(struct northd_data *data)
          .mac_binding_timestamp = true,
          .ct_lb_related = true,
          .fdb_timestamp = true,
 +        .ls_dpg_column = true,
++        .ct_commit_nat_v2 = true,
      };
      data->ovn_internal_version_changed = false;
      sset_init(&data->svc_monitor_lsps);
-@@ -18051,13 +18189,15 @@ static void
+@@ -18051,13 +18199,15 @@ static void
  handle_cr_port_binding_changes(const struct sbrec_port_binding *sb,
                  struct ovn_port *orp)
  {
@@ -3908,18 +5297,19 @@ index 04afe62a8..8aeca33f8 100644
  }
  
 diff --git a/northd/northd.h b/northd/northd.h
-index 4d030b10d..76dfc392d 100644
+index 4d030b10d..5bde4bb16 100644
 --- a/northd/northd.h
 +++ b/northd/northd.h
-@@ -70,6 +70,7 @@ struct chassis_features {
+@@ -70,6 +70,8 @@ struct chassis_features {
      bool mac_binding_timestamp;
      bool ct_lb_related;
      bool fdb_timestamp;
 +    bool ls_dpg_column;
++    bool ct_commit_nat_v2;
  };
  
  /* A collection of datapaths. E.g. all logical switch datapaths, or all
-@@ -365,7 +366,11 @@ const char *northd_get_svc_monitor_mac(void);
+@@ -365,7 +367,11 @@ const char *northd_get_svc_monitor_mac(void);
  const struct ovn_datapath *northd_get_datapath_for_port(
      const struct hmap *ls_ports, const char *port_name);
  void sync_lbs(struct ovsdb_idl_txn *, const struct sbrec_load_balancer_table *,
@@ -3933,10 +5323,50 @@ index 4d030b10d..76dfc392d 100644
  void sync_pbs(struct ovsdb_idl_txn *, struct hmap *ls_ports);
  bool sync_pbs_for_northd_ls_changes(struct tracked_ls_changes *);
 diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
-index df8ed6750..97718821f 100644
+index df8ed6750..e6970d6b3 100644
 --- a/northd/ovn-northd.8.xml
 +++ b/northd/ovn-northd.8.xml
-@@ -399,14 +399,16 @@
+@@ -341,7 +341,7 @@
+       <li>
+         For each (enabled) vtep logical port, a priority 70 flow is added which
+         matches on all packets and applies the action
+-        <code>next(pipeline=ingress, table=S_SWITCH_IN_L2_LKUP) = 1;</code>
++        <code>next(pipeline=ingress, table=S_SWITCH_IN_L3_LKUP) = 1;</code>
+         to skip most stages of ingress pipeline and go directly to ingress L2
+         lookup table to determine the output port. Packets from VTEP (RAMP)
+         switch should not be subjected to any ACL checks. Egress pipeline will
+@@ -372,6 +372,30 @@
+ 
+     <h3>Ingress Table 1: Ingress Port Security - Apply</h3>
+ 
++    <p>
++      For each logical switch port <var>P</var> of type router connected to a
++      gw router a priority-120 flow that matches 'recirculated' icmp{4,6} error
++      'packet too big' and <code>eth.src == <var>D</var> &amp;&amp;
++      outport == <var>P</var> &amp;&amp; flags.tunnel_rx == 1</code> where
++      <var>D</var> is the peer logical router port <var>RP</var> mac address,
++      swaps inport and outport and applies the action <code>
++      next(pipeline=S_SWITCH_IN_L2_LKUP)</code>.
++    </p>
++
++    <p>
++      For each logical switch port <var>P</var> of type router connected to a
++      distributed router a priority-120 flow that matches 'recirculated'
++      icmp{4,6} error 'packet too big' and <code>eth.dst == <var>D</var>
++      &amp;&amp; flags.tunnel_rx == 1</code> where <var>D</var> is the peer
++      logical router port <var>RP</var> mac address, swaps inport and outport
++      and applies the action <code> next(pipeline=S_SWITCH_IN_L2_LKUP)</code>.
++    </p>
++
++    <p>
++      This table adds a priority-110 flow that matches 'recirculated' icmp{4,6}
++      error 'packet too big' to drop the packet.
++    </p>
++
+     <p>
+       This table drops the packets if the port security check failed
+       in the previous stage i.e the register bit
+@@ -399,14 +423,16 @@
      <p>
        This table looks up the MAC learning table of the logical switch
        datapath to check if the <code>port-mac</code> pair is present
@@ -3956,7 +5386,7 @@ index df8ed6750..97718821f 100644
            is disabled and 'unknown' address set following flow
            is added.
          </p>
-@@ -420,6 +422,22 @@
+@@ -420,6 +446,22 @@
          </ul>
        </li>
  
@@ -3979,7 +5409,7 @@ index df8ed6750..97718821f 100644
        <li>
          One priority-0 fallback flow that matches all packets and advances to
          the next table.
-@@ -429,17 +447,20 @@
+@@ -429,17 +471,20 @@
      <h3>Ingress Table 3: Learn MAC of 'unknown' ports.</h3>
  
      <p>
@@ -4005,8 +5435,30 @@ index df8ed6750..97718821f 100644
            is added.
          </p>
  
+@@ -2442,6 +2487,21 @@ output;
+           (LBs, NAT).
+         </p>
+ 
++        <p>
++          For each gateway port <var>GW</var> on a distributed logical router
++          a priority-120 flow that matches 'recirculated' icmp{4,6} error
++          'packet too big' and <code>eth.dst == <var>D</var> &amp;&amp;
++          !is_chassis_resident(<var> cr-GW</var>)</code> where <var>D</var>
++          is the gateway port mac address and <var>cr-GW</var> is the chassis
++          resident port of <var>GW</var>, swap inport and outport and stores
++          <var>GW</var> as inport.
++        </p>
++
++        <p>
++          This table adds a priority-110 flow that matches 'recirculated'
++          icmp{4,6} error 'packet too big' to drop the packet.
++        </p>
++
+         <p>
+           For a distributed logical router or for gateway router where
+           the port is configured with <code>options:gateway_mtu</code>
 diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
-index 68fc8836e..b2c5552f6 100644
+index 68fc8836e..9d9a1d720 100644
 --- a/northd/ovn-northd.c
 +++ b/northd/ovn-northd.c
 @@ -47,7 +47,6 @@
@@ -4017,7 +5469,22 @@ index 68fc8836e..b2c5552f6 100644
  static unixctl_cb_func ovn_northd_pause;
  static unixctl_cb_func ovn_northd_resume;
  static unixctl_cb_func ovn_northd_is_paused;
-@@ -752,7 +751,7 @@ main(int argc, char *argv[])
+@@ -612,6 +611,14 @@ parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED,
+             ssl_ca_cert_file = optarg;
+             break;
+ 
++        case OPT_SSL_PROTOCOLS:
++            stream_ssl_set_protocols(optarg);
++            break;
++
++        case OPT_SSL_CIPHERS:
++            stream_ssl_set_ciphers(optarg);
++            break;
++
+         case 'd':
+             ovnsb_db = optarg;
+             break;
+@@ -752,7 +759,7 @@ main(int argc, char *argv[])
      int res = EXIT_SUCCESS;
      struct unixctl_server *unixctl;
      int retval;
@@ -4026,7 +5493,7 @@ index 68fc8836e..b2c5552f6 100644
      int n_threads = 1;
      struct northd_state state = {
          .had_lock = false,
-@@ -774,7 +773,8 @@ main(int argc, char *argv[])
+@@ -774,7 +781,8 @@ main(int argc, char *argv[])
      if (retval) {
          exit(EXIT_FAILURE);
      }
@@ -4036,7 +5503,7 @@ index 68fc8836e..b2c5552f6 100644
      unixctl_command_register("pause", "", 0, 0, ovn_northd_pause, &state);
      unixctl_command_register("resume", "", 0, 0, ovn_northd_resume, &state);
      unixctl_command_register("is-paused", "", 0, 0, ovn_northd_is_paused,
-@@ -868,6 +868,8 @@ main(int argc, char *argv[])
+@@ -868,6 +876,8 @@ main(int argc, char *argv[])
      stopwatch_create(LFLOWS_IGMP_STOPWATCH_NAME, SW_MS);
      stopwatch_create(LFLOWS_DP_GROUPS_STOPWATCH_NAME, SW_MS);
      stopwatch_create(LFLOWS_TO_SB_STOPWATCH_NAME, SW_MS);
@@ -4045,7 +5512,7 @@ index 68fc8836e..b2c5552f6 100644
  
      /* Initialize incremental processing engine for ovn-northd */
      inc_proc_northd_init(&ovnnb_idl_loop, &ovnsb_idl_loop);
-@@ -878,11 +880,9 @@ main(int argc, char *argv[])
+@@ -878,11 +888,9 @@ main(int argc, char *argv[])
      run_update_worker_pool(n_threads);
  
      /* Main loop. */
@@ -4058,7 +5525,7 @@ index 68fc8836e..b2c5552f6 100644
          update_ssl_config();
          memory_run();
          if (memory_should_report()) {
-@@ -1024,7 +1024,7 @@ main(int argc, char *argv[])
+@@ -1024,7 +1032,7 @@ main(int argc, char *argv[])
          unixctl_server_run(unixctl);
          unixctl_server_wait(unixctl);
          memory_wait();
@@ -4067,7 +5534,7 @@ index 68fc8836e..b2c5552f6 100644
              poll_immediate_wake();
          }
  
-@@ -1057,15 +1057,16 @@ main(int argc, char *argv[])
+@@ -1057,15 +1065,16 @@ main(int argc, char *argv[])
          stopwatch_stop(NORTHD_LOOP_STOPWATCH_NAME, time_msec());
          poll_block();
          if (should_service_stop()) {
@@ -4086,7 +5553,7 @@ index 68fc8836e..b2c5552f6 100644
      service_stop();
      run_update_worker_pool(0);
      ovsrcu_exit();
-@@ -1073,16 +1074,6 @@ main(int argc, char *argv[])
+@@ -1073,16 +1082,6 @@ main(int argc, char *argv[])
      exit(res);
  }
  
@@ -4146,7 +5613,7 @@ index 7b20aa21b..a512e00e5 100644
                       "type": {"key": "string",
                                "value": "string",
 diff --git a/ovn-sb.xml b/ovn-sb.xml
-index 46aedf973..519b9da8f 100644
+index 46aedf973..3ad7409a3 100644
 --- a/ovn-sb.xml
 +++ b/ovn-sb.xml
 @@ -1721,9 +1721,12 @@
@@ -4164,7 +5631,26 @@ index 46aedf973..519b9da8f 100644
            </p>
  
            <p>
-@@ -4888,10 +4891,22 @@ tcp.flags = RST;
+@@ -4765,8 +4768,7 @@ tcp.flags = RST;
+       Each row in this table configures monitoring a service for its liveness.
+       The service can be an IPv4 TCP or UDP
+       service. <code>ovn-controller</code> periodically sends out service
+-      monitor packets and updates the status of the service. Service monitoring
+-      for IPv6 services is not supported.
++      monitor packets and updates the status of the service.
+     </p>
+ 
+     <p>
+@@ -4803,7 +4805,7 @@ tcp.flags = RST;
+       </column>
+ 
+       <column name="src_ip">
+-        Source IPv4 address to use in the service monitor packet.
++        Source IPv4 or IPv6 address to use in the service monitor packet.
+       </column>
+ 
+       <column name="options" key="interval" type='{"type": "integer"}'>
+@@ -4888,10 +4890,22 @@ tcp.flags = RST;
      </column>
  
      <column name="datapath_group">
@@ -4269,10 +5755,868 @@ index 26f319705..e7322fff4 100755
 +    Subject: netdev: This is a way to long commit summary and therefor it should report a WARNING!"
 +
 +AT_CLEANUP
+diff --git a/tests/multinode.at b/tests/multinode.at
+index 2b199b4bc..0187382be 100644
+--- a/tests/multinode.at
++++ b/tests/multinode.at
+@@ -42,7 +42,6 @@ M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | F
+ 3 packets transmitted, 3 received, 0% packet loss, time 0ms
+ ])
+ 
+-
+ # Create the second logical switch with one port
+ check multinode_nbctl ls-add sw1
+ check multinode_nbctl lsp-add sw1 sw1-port1
+@@ -72,3 +71,815 @@ M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | F
+ ])
+ 
+ AT_CLEANUP
++
++AT_SETUP([ovn multinode pmtu - distributed router - geneve])
++
++# Check that ovn-fake-multinode setup is up and running
++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
++
++# Reset geneve tunnels
++for c in ovn-chassis-1 ovn-chassis-2 ovn-gw-1
++do
++    m_as $c ovs-vsctl set open . external-ids:ovn-encap-type=geneve
++done
++
++OVS_WAIT_UNTIL([m_as ovn-chassis-1 ip link show | grep -q genev_sys])
++OVS_WAIT_UNTIL([m_as ovn-chassis-2 ip link show | grep -q genev_sys])
++OVS_WAIT_UNTIL([m_as ovn-gw-1 ip link show | grep -q genev_sys])
++
++# Test East-West switching
++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"
++check multinode_nbctl lsp-add sw0 sw0-port2
++check multinode_nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 1000::4"
++
++m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1 50:54:00:00:00:03 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2 50:54:00:00:00:04 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a
++
++m_wait_for_ports_up
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Create the second logical switch with one port
++check multinode_nbctl ls-add sw1
++check multinode_nbctl lsp-add sw1 sw1-port1
++check multinode_nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3"
++
++# Create a logical router and attach both logical switches
++check multinode_nbctl lr-add lr0
++check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64
++check multinode_nbctl lsp-add sw0 sw0-lr0
++check multinode_nbctl lsp-set-type sw0-lr0 router
++check multinode_nbctl lsp-set-addresses sw0-lr0 router
++check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++check multinode_nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64
++check multinode_nbctl lsp-add sw1 sw1-lr0
++check multinode_nbctl lsp-set-type sw1-lr0 router
++check multinode_nbctl lsp-set-addresses sw1-lr0 router
++check multinode_nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
++
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw1-port1 sw1p1 40:54:00:00:00:03 20.0.0.3 24 20.0.0.1 2000::3/64 2000::a
++
++# create exteranl connection for N/S traffic
++check multinode_nbctl ls-add public
++check multinode_nbctl lsp-add public ln-lublic
++check multinode_nbctl lsp-set-type ln-lublic localnet
++check multinode_nbctl lsp-set-addresses ln-lublic unknown
++check multinode_nbctl lsp-set-options ln-lublic network_name=public
++
++check multinode_nbctl lrp-add lr0 lr0-public 00:11:22:00:ff:01 172.20.0.100/24
++check multinode_nbctl lsp-add public public-lr0
++check multinode_nbctl lsp-set-type public-lr0 router
++check multinode_nbctl lsp-set-addresses public-lr0 router
++check multinode_nbctl lsp-set-options public-lr0 router-port=lr0-public
++check multinode_nbctl lrp-set-gateway-chassis lr0-public ovn-gw-1 10
++check multinode_nbctl lr-route-add lr0 0.0.0.0/0 172.20.0.1
++
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 10.0.0.0/24
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 20.0.0.0/24
++
++# create some ACLs
++check multinode_nbctl acl-add sw0 from-lport 1002 'ip4 || ip6'  allow-related
++check multinode_nbctl acl-add sw1 from-lport 1002 'ip4 || ip6'  allow-related
++
++m_as ovn-gw-1 ip netns add ovn-ext0
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext0 -- set interface ext0 type=internal
++m_as ovn-gw-1 ip link set ext0 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext0 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.0.1/24 dev ext0
++
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext1 -- set interface ext1 type=internal
++m_as ovn-gw-1 ip link set ext1 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext1 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.1.1/24 dev ext1
++
++m_as ovn-gw-1 ip netns add ovn-ext2
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext2 -- set interface ext2 type=internal
++m_as ovn-gw-1 ip link set ext2 netns ovn-ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip link set ext2 up
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip addr add 172.20.1.2/24 dev ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip route add default via 172.20.1.1 dev ext2
++
++m_as ovn-gw-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++
++m_wait_for_ports_up sw1-port1
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Change ptmu for the geneve tunnel
++m_as ovn-chassis-1 ip route change 170.168.0.0/16 mtu 1200 dev eth1
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 5 -s 1300 -M do 20.0.0.3 2>&1 |grep -q "message too long, mtu=1142"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++m_as ovn-chassis-1 ip route change 170.168.0.0/16 mtu 1400 dev eth1
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping6 -c 5 -s 1450 -M do 2000::3 2>&1 |grep -q "message too long, mtu: 1342"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.1.2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-gw-1], [ovn-ext0], [ip link set dev ext1 mtu 1000])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 10 -s 1300 -M do 172.20.1.2 2>&1 |grep -q "mtu = 1000"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.1.2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++AT_CLEANUP
++
++AT_SETUP([ovn multinode pmtu - distributed router - vxlan])
++
++# Check that ovn-fake-multinode setup is up and running
++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
++
++# Reset vxlan tunnels
++for c in ovn-chassis-1 ovn-chassis-2 ovn-gw-1
++do
++    m_as $c ovs-vsctl set open . external-ids:ovn-encap-type=vxlan
++done
++
++OVS_WAIT_UNTIL([m_as ovn-chassis-1 ip link show | grep -q vxlan_sys])
++OVS_WAIT_UNTIL([m_as ovn-chassis-2 ip link show | grep -q vxlan_sys])
++OVS_WAIT_UNTIL([m_as ovn-gw-1 ip link show | grep -q vxlan_sys])
++
++# Test East-West switching
++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"
++check multinode_nbctl lsp-add sw0 sw0-port2
++check multinode_nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 1000::4"
++
++m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1 50:54:00:00:00:03 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2 50:54:00:00:00:04 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a
++
++m_wait_for_ports_up
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Create the second logical switch with one port
++check multinode_nbctl ls-add sw1
++check multinode_nbctl lsp-add sw1 sw1-port1
++check multinode_nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3"
++
++# Create a logical router and attach both logical switches
++check multinode_nbctl lr-add lr0
++check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64
++check multinode_nbctl lsp-add sw0 sw0-lr0
++check multinode_nbctl lsp-set-type sw0-lr0 router
++check multinode_nbctl lsp-set-addresses sw0-lr0 router
++check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++check multinode_nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64
++check multinode_nbctl lsp-add sw1 sw1-lr0
++check multinode_nbctl lsp-set-type sw1-lr0 router
++check multinode_nbctl lsp-set-addresses sw1-lr0 router
++check multinode_nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
++
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw1-port1 sw1p1 40:54:00:00:00:03 20.0.0.3 24 20.0.0.1 2000::3/64 2000::a
++
++# create exteranl connection for N/S traffic
++check multinode_nbctl ls-add public
++check multinode_nbctl lsp-add public ln-lublic
++check multinode_nbctl lsp-set-type ln-lublic localnet
++check multinode_nbctl lsp-set-addresses ln-lublic unknown
++check multinode_nbctl lsp-set-options ln-lublic network_name=public
++
++check multinode_nbctl lrp-add lr0 lr0-public 00:11:22:00:ff:01 172.20.0.100/24
++check multinode_nbctl lsp-add public public-lr0
++check multinode_nbctl lsp-set-type public-lr0 router
++check multinode_nbctl lsp-set-addresses public-lr0 router
++check multinode_nbctl lsp-set-options public-lr0 router-port=lr0-public
++check multinode_nbctl lrp-set-gateway-chassis lr0-public ovn-gw-1 10
++check multinode_nbctl lr-route-add lr0 0.0.0.0/0 172.20.0.1
++
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 10.0.0.0/24
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 20.0.0.0/24
++
++# create some ACLs
++check multinode_nbctl acl-add sw0 from-lport 1002 'ip4 || ip6'  allow-related
++check multinode_nbctl acl-add sw1 from-lport 1002 'ip4 || ip6'  allow-related
++
++m_as ovn-gw-1 ip netns add ovn-ext0
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext0 -- set interface ext0 type=internal
++m_as ovn-gw-1 ip link set ext0 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext0 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.0.1/24 dev ext0
++
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext1 -- set interface ext1 type=internal
++m_as ovn-gw-1 ip link set ext1 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext1 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.1.1/24 dev ext1
++
++m_as ovn-gw-1 ip netns add ovn-ext2
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext2 -- set interface ext2 type=internal
++m_as ovn-gw-1 ip link set ext2 netns ovn-ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip link set ext2 up
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip addr add 172.20.1.2/24 dev ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip route add default via 172.20.1.1 dev ext2
++
++m_as ovn-gw-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++
++m_wait_for_ports_up sw1-port1
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Change ptmu for the vxlan tunnel
++m_as ovn-chassis-1 ip route change 170.168.0.0/16 mtu 1200 dev eth1
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 5 -s 1300 -M do 20.0.0.3 2>&1 |grep -q "message too long, mtu=1150"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++M_NS_CHECK_EXEC([ovn-gw-1], [ovn-ext0], [ip link set dev ext1 mtu 1100])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.1.2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 20 -i 0.5 -s 1300 -M do 172.20.1.2 2>&1 |grep -q "mtu = 1150"])
++
++AT_CLEANUP
++
++AT_SETUP([ovn multinode pmtu - gw_router_port - geneve])
++
++# Check that ovn-fake-multinode setup is up and running
++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
++
++# Reset geneve tunnels
++for c in ovn-chassis-1 ovn-chassis-2 ovn-gw-1
++do
++    m_as $c ovs-vsctl set open . external-ids:ovn-encap-type=geneve
++done
++
++OVS_WAIT_UNTIL([m_as ovn-chassis-1 ip link show | grep -q genev_sys])
++OVS_WAIT_UNTIL([m_as ovn-chassis-2 ip link show | grep -q genev_sys])
++OVS_WAIT_UNTIL([m_as ovn-gw-1 ip link show | grep -q genev_sys])
++
++# Test East-West switching
++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"
++check multinode_nbctl lsp-add sw0 sw0-port2
++check multinode_nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 1000::4"
++
++m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1 50:54:00:00:00:03 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2 50:54:00:00:00:04 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a
++
++m_wait_for_ports_up
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Create the second logical switch with one port
++check multinode_nbctl ls-add sw1
++check multinode_nbctl lsp-add sw1 sw1-port1
++check multinode_nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3"
++
++# Create a logical router and attach both logical switches
++check multinode_nbctl lr-add lr0
++check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64
++check multinode_nbctl lsp-add sw0 sw0-lr0
++check multinode_nbctl lsp-set-type sw0-lr0 router
++check multinode_nbctl lsp-set-addresses sw0-lr0 router
++check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++check multinode_nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64
++check multinode_nbctl lsp-add sw1 sw1-lr0
++check multinode_nbctl lsp-set-type sw1-lr0 router
++check multinode_nbctl lsp-set-addresses sw1-lr0 router
++check multinode_nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
++
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw1-port1 sw1p1 40:54:00:00:00:03 20.0.0.3 24 20.0.0.1 2000::3/64 2000::a
++
++# create exteranl connection for N/S traffic
++check multinode_nbctl ls-add public
++check multinode_nbctl lsp-add public ln-lublic
++check multinode_nbctl lsp-set-type ln-lublic localnet
++check multinode_nbctl lsp-set-addresses ln-lublic unknown
++check multinode_nbctl lsp-set-options ln-lublic network_name=public
++
++check multinode_nbctl lrp-add lr0 lr0-public 00:11:22:00:ff:01 172.20.0.100/24
++check multinode_nbctl lsp-add public public-lr0
++check multinode_nbctl lsp-set-type public-lr0 router
++check multinode_nbctl lsp-set-addresses public-lr0 router
++check multinode_nbctl lsp-set-options public-lr0 router-port=lr0-public
++check multinode_nbctl lrp-set-gateway-chassis lr0-public ovn-gw-1 10
++check multinode_nbctl lr-route-add lr0 0.0.0.0/0 172.20.0.1
++
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 10.0.0.0/24
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 20.0.0.0/24
++
++check multinode_nbctl lrp-set-gateway-chassis lr0-sw0 ovn-chassis-1 10
++check multinode_nbctl lrp-set-gateway-chassis lr0-sw1 ovn-chassis-2 10
++
++# create some ACLs
++check multinode_nbctl acl-add sw0 from-lport 1002 'ip4 || ip6'  allow-related
++check multinode_nbctl acl-add sw1 from-lport 1002 'ip4 || ip6'  allow-related
++
++m_as ovn-gw-1 ip netns add ovn-ext0
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext0 -- set interface ext0 type=internal
++m_as ovn-gw-1 ip link set ext0 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext0 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.0.1/24 dev ext0
++
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext1 -- set interface ext1 type=internal
++m_as ovn-gw-1 ip link set ext1 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext1 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.1.1/24 dev ext1
++
++m_as ovn-gw-1 ip netns add ovn-ext2
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext2 -- set interface ext2 type=internal
++m_as ovn-gw-1 ip link set ext2 netns ovn-ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip link set ext2 up
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip addr add 172.20.1.2/24 dev ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip route add default via 172.20.1.1 dev ext2
++
++m_as ovn-gw-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++
++m_wait_for_ports_up sw1-port1
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++m_as ovn-chassis-1 ip route change 170.168.0.0/16 mtu 1200 dev eth1
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 5 -s 1300 -M do 20.0.0.3 2>&1 |grep -q "message too long, mtu=1142"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++m_as ovn-chassis-1 ip route change 170.168.0.0/16 mtu 1400 dev eth1
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping6 -c 5 -s 1450 -M do 2000::3 2>&1 |grep -q "message too long, mtu: 1342"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.1.2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-gw-1], [ovn-ext0], [ip link set dev ext1 mtu 1100])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 20 -i 0.5 -s 1300 -M do 172.20.1.2 2>&1 |grep -q "mtu = 1100"])
++
++AT_CLEANUP
++
++AT_SETUP([ovn multinode pmtu - gw_router_port - vxlan])
++
++# Check that ovn-fake-multinode setup is up and running
++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
++
++# Reset geneve tunnels
++for c in ovn-chassis-1 ovn-chassis-2 ovn-gw-1
++do
++    m_as $c ovs-vsctl set open . external-ids:ovn-encap-type=vxlan
++done
++
++OVS_WAIT_UNTIL([m_as ovn-chassis-1 ip link show | grep -q vxlan_sys])
++OVS_WAIT_UNTIL([m_as ovn-chassis-2 ip link show | grep -q vxlan_sys])
++OVS_WAIT_UNTIL([m_as ovn-gw-1 ip link show | grep -q vxlan_sys])
++
++# Test East-West switching
++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"
++check multinode_nbctl lsp-add sw0 sw0-port2
++check multinode_nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 1000::4"
++
++m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1 50:54:00:00:00:03 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2 50:54:00:00:00:04 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a
++
++m_wait_for_ports_up
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Create the second logical switch with one port
++check multinode_nbctl ls-add sw1
++check multinode_nbctl lsp-add sw1 sw1-port1
++check multinode_nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3"
++
++# Create a logical router and attach both logical switches
++check multinode_nbctl lr-add lr0
++check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64
++check multinode_nbctl lsp-add sw0 sw0-lr0
++check multinode_nbctl lsp-set-type sw0-lr0 router
++check multinode_nbctl lsp-set-addresses sw0-lr0 router
++check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++check multinode_nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64
++check multinode_nbctl lsp-add sw1 sw1-lr0
++check multinode_nbctl lsp-set-type sw1-lr0 router
++check multinode_nbctl lsp-set-addresses sw1-lr0 router
++check multinode_nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
++
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw1-port1 sw1p1 40:54:00:00:00:03 20.0.0.3 24 20.0.0.1 2000::3/64 2000::a
++
++# create exteranl connection for N/S traffic
++check multinode_nbctl ls-add public
++check multinode_nbctl lsp-add public ln-lublic
++check multinode_nbctl lsp-set-type ln-lublic localnet
++check multinode_nbctl lsp-set-addresses ln-lublic unknown
++check multinode_nbctl lsp-set-options ln-lublic network_name=public
++
++check multinode_nbctl lrp-add lr0 lr0-public 00:11:22:00:ff:01 172.20.0.100/24
++check multinode_nbctl lsp-add public public-lr0
++check multinode_nbctl lsp-set-type public-lr0 router
++check multinode_nbctl lsp-set-addresses public-lr0 router
++check multinode_nbctl lsp-set-options public-lr0 router-port=lr0-public
++check multinode_nbctl lrp-set-gateway-chassis lr0-public ovn-gw-1 10
++check multinode_nbctl lr-route-add lr0 0.0.0.0/0 172.20.0.1
++
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 10.0.0.0/24
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 20.0.0.0/24
++
++check multinode_nbctl lrp-set-gateway-chassis lr0-sw0 ovn-chassis-1 10
++check multinode_nbctl lrp-set-gateway-chassis lr0-sw1 ovn-chassis-2 10
++
++# create some ACLs
++check multinode_nbctl acl-add sw0 from-lport 1002 'ip4 || ip6'  allow-related
++check multinode_nbctl acl-add sw1 from-lport 1002 'ip4 || ip6'  allow-related
++
++m_as ovn-gw-1 ip netns add ovn-ext0
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext0 -- set interface ext0 type=internal
++m_as ovn-gw-1 ip link set ext0 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext0 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.0.1/24 dev ext0
++
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext1 -- set interface ext1 type=internal
++m_as ovn-gw-1 ip link set ext1 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext1 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.1.1/24 dev ext1
++
++m_as ovn-gw-1 ip netns add ovn-ext2
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext2 -- set interface ext2 type=internal
++m_as ovn-gw-1 ip link set ext2 netns ovn-ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip link set ext2 up
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip addr add 172.20.1.2/24 dev ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip route add default via 172.20.1.1 dev ext2
++
++m_as ovn-gw-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++
++m_wait_for_ports_up sw1-port1
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++m_as ovn-chassis-1 ip route change 170.168.0.0/16 mtu 1200 dev eth1
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 5 -s 1300 -M do 20.0.0.3 2>&1 |grep -q "message too long, mtu=1150"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++M_NS_CHECK_EXEC([ovn-gw-1], [ovn-ext0], [ip link set dev ext1 mtu 1100])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.1.2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 20 -i 0.5 -s 1300 -M do 172.20.1.2 2>&1 |grep -q "mtu = 1150"])
++
++AT_CLEANUP
++
++AT_SETUP([ovn multinode pmtu - gw router - geneve])
++
++# Check that ovn-fake-multinode setup is up and running
++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
++
++# Reset geneve tunnels
++for c in ovn-chassis-1 ovn-chassis-2 ovn-gw-1
++do
++    m_as $c ovs-vsctl set open . external-ids:ovn-encap-type=geneve
++done
++
++OVS_WAIT_UNTIL([m_as ovn-chassis-1 ip link show | grep -q genev_sys])
++OVS_WAIT_UNTIL([m_as ovn-chassis-2 ip link show | grep -q genev_sys])
++OVS_WAIT_UNTIL([m_as ovn-gw-1 ip link show | grep -q genev_sys])
++
++# Test East-West switching
++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"
++check multinode_nbctl lsp-add sw0 sw0-port2
++check multinode_nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 1000::4"
++
++m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1 50:54:00:00:00:03 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2 50:54:00:00:00:04 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a
++
++m_wait_for_ports_up
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Create the second logical switch with one port
++check multinode_nbctl ls-add sw1
++check multinode_nbctl lsp-add sw1 sw1-port1
++check multinode_nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3"
++
++# Create a logical router and attach both logical switches
++check multinode_nbctl lr-add lr0 -- set Logical_Router lr0 options:chassis=ovn-gw-1
++check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64
++check multinode_nbctl lsp-add sw0 sw0-lr0
++check multinode_nbctl lsp-set-type sw0-lr0 router
++check multinode_nbctl lsp-set-addresses sw0-lr0 router
++check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++check multinode_nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64
++check multinode_nbctl lsp-add sw1 sw1-lr0
++check multinode_nbctl lsp-set-type sw1-lr0 router
++check multinode_nbctl lsp-set-addresses sw1-lr0 router
++check multinode_nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
++
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw1-port1 sw1p1 40:54:00:00:00:03 20.0.0.3 24 20.0.0.1 2000::3/64 2000::a
++
++# create exteranl connection for N/S traffic
++check multinode_nbctl ls-add public
++check multinode_nbctl lsp-add public ln-lublic
++check multinode_nbctl lsp-set-type ln-lublic localnet
++check multinode_nbctl lsp-set-addresses ln-lublic unknown
++check multinode_nbctl lsp-set-options ln-lublic network_name=public
++
++check multinode_nbctl lrp-add lr0 lr0-public 00:11:22:00:ff:01 172.20.0.100/24
++check multinode_nbctl lsp-add public public-lr0
++check multinode_nbctl lsp-set-type public-lr0 router
++check multinode_nbctl lsp-set-addresses public-lr0 router
++check multinode_nbctl lsp-set-options public-lr0 router-port=lr0-public
++check multinode_nbctl lr-route-add lr0 0.0.0.0/0 172.20.0.1
++
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 10.0.0.0/24
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 20.0.0.0/24
++
++# create some ACLs
++check multinode_nbctl acl-add sw0 from-lport 1002 'ip4 || ip6'  allow-related
++check multinode_nbctl acl-add sw1 from-lport 1002 'ip4 || ip6'  allow-related
++
++m_as ovn-gw-1 ip netns add ovn-ext0
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext0 -- set interface ext0 type=internal
++m_as ovn-gw-1 ip link set ext0 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext0 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.0.1/24 dev ext0
++
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext1 -- set interface ext1 type=internal
++m_as ovn-gw-1 ip link set ext1 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext1 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.1.1/24 dev ext1
++
++m_as ovn-gw-1 ip netns add ovn-ext2
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext2 -- set interface ext2 type=internal
++m_as ovn-gw-1 ip link set ext2 netns ovn-ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip link set ext2 up
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip addr add 172.20.1.2/24 dev ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip route add default via 172.20.1.1 dev ext2
++
++m_as ovn-gw-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++
++m_wait_for_ports_up sw1-port1
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++m_as ovn-chassis-1 ip route change 170.168.0.0/16 mtu 1200 dev eth1
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 5 -s 1300 -M do 20.0.0.3 2>&1 |grep -q "message too long, mtu=1142"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++m_as ovn-chassis-1 ip route change 170.168.0.0/16 mtu 1400 dev eth1
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping6 -c 5 -s 1450 -M do 2000::3 2>&1 |grep -q "message too long, mtu: 1342"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.1.2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-gw-1], [ovn-ext0], [ip link set dev ext1 mtu 1100])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 20 -i 0.5 -s 1300 -M do 172.20.1.2 2>&1 |grep -q "mtu = 1100"])
++
++AT_CLEANUP
++
++AT_SETUP([ovn multinode pmtu - gw router - vxlan])
++
++# Check that ovn-fake-multinode setup is up and running
++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
++
++# Reset geneve tunnels
++for c in ovn-chassis-1 ovn-chassis-2 ovn-gw-1
++do
++    m_as $c ovs-vsctl set open . external-ids:ovn-encap-type=vxlan
++done
++
++OVS_WAIT_UNTIL([m_as ovn-chassis-1 ip link show | grep -q vxlan_sys])
++OVS_WAIT_UNTIL([m_as ovn-chassis-2 ip link show | grep -q vxlan_sys])
++OVS_WAIT_UNTIL([m_as ovn-gw-1 ip link show | grep -q vxlan_sys])
++
++# Test East-West switching
++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"
++check multinode_nbctl lsp-add sw0 sw0-port2
++check multinode_nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 1000::4"
++
++m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1 50:54:00:00:00:03 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2 50:54:00:00:00:04 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a
++
++m_wait_for_ports_up
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Create the second logical switch with one port
++check multinode_nbctl ls-add sw1
++check multinode_nbctl lsp-add sw1 sw1-port1
++check multinode_nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3"
++
++# Create a logical router and attach both logical switches
++check multinode_nbctl lr-add lr0 -- set Logical_Router lr0 options:chassis=ovn-gw-1
++check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64
++check multinode_nbctl lsp-add sw0 sw0-lr0
++check multinode_nbctl lsp-set-type sw0-lr0 router
++check multinode_nbctl lsp-set-addresses sw0-lr0 router
++check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++check multinode_nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64
++check multinode_nbctl lsp-add sw1 sw1-lr0
++check multinode_nbctl lsp-set-type sw1-lr0 router
++check multinode_nbctl lsp-set-addresses sw1-lr0 router
++check multinode_nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
++
++m_as ovn-chassis-2 /data/create_fake_vm.sh sw1-port1 sw1p1 40:54:00:00:00:03 20.0.0.3 24 20.0.0.1 2000::3/64 2000::a
++
++# create exteranl connection for N/S traffic
++check multinode_nbctl ls-add public
++check multinode_nbctl lsp-add public ln-lublic
++check multinode_nbctl lsp-set-type ln-lublic localnet
++check multinode_nbctl lsp-set-addresses ln-lublic unknown
++check multinode_nbctl lsp-set-options ln-lublic network_name=public
++
++check multinode_nbctl lrp-add lr0 lr0-public 00:11:22:00:ff:01 172.20.0.100/24
++check multinode_nbctl lsp-add public public-lr0
++check multinode_nbctl lsp-set-type public-lr0 router
++check multinode_nbctl lsp-set-addresses public-lr0 router
++check multinode_nbctl lsp-set-options public-lr0 router-port=lr0-public
++check multinode_nbctl lr-route-add lr0 0.0.0.0/0 172.20.0.1
++
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 10.0.0.0/24
++check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 20.0.0.0/24
++
++# create some ACLs
++check multinode_nbctl acl-add sw0 from-lport 1002 'ip4 || ip6'  allow-related
++check multinode_nbctl acl-add sw1 from-lport 1002 'ip4 || ip6'  allow-related
++
++m_as ovn-gw-1 ip netns add ovn-ext0
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext0 -- set interface ext0 type=internal
++m_as ovn-gw-1 ip link set ext0 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext0 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.0.1/24 dev ext0
++
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext1 -- set interface ext1 type=internal
++m_as ovn-gw-1 ip link set ext1 netns ovn-ext0
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip link set ext1 up
++m_as ovn-gw-1 ip netns exec ovn-ext0 ip addr add 172.20.1.1/24 dev ext1
++
++m_as ovn-gw-1 ip netns add ovn-ext2
++m_as ovn-gw-1 ovs-vsctl add-port br-ex ext2 -- set interface ext2 type=internal
++m_as ovn-gw-1 ip link set ext2 netns ovn-ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip link set ext2 up
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip addr add 172.20.1.2/24 dev ext2
++m_as ovn-gw-1 ip netns exec ovn-ext2 ip route add default via 172.20.1.1 dev ext2
++
++m_as ovn-gw-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++m_as ovn-chassis-2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-ex
++
++m_wait_for_ports_up sw1-port1
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++m_as ovn-chassis-1 ip route change 170.168.0.0/16 mtu 1200 dev eth1
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 5 -s 1300 -M do 20.0.0.3 2>&1 |grep -q "message too long, mtu=1150"])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route flush dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add 10.0.0.0/24 dev sw0p1])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route add default via 10.0.0.1 dev sw0p1])
++
++M_NS_CHECK_EXEC([ovn-gw-1], [ovn-ext0], [ip link set dev ext1 mtu 1100])
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 172.20.1.2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -c 20 -i 0.5 -s 1300 -M do 172.20.1.2 2>&1 |grep -q "mtu = 1150"])
++
++AT_CLEANUP
+diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
+index 07ef1d092..bccedbaf7 100644
+--- a/tests/ofproto-macros.at
++++ b/tests/ofproto-macros.at
+@@ -200,14 +200,14 @@ m4_define([_OVS_VSWITCHD_START],
+    AT_CHECK([[sed < stderr '
+ /vlog|INFO|opened log file/d
+ /ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']])
+-   AT_CAPTURE_FILE([ovsdb-server.log])
++  # AT_CAPTURE_FILE([ovsdb-server.log])
+ 
+    dnl Initialize database.
+    AT_CHECK([ovs-vsctl --no-wait init $2])
+ 
+    dnl Start ovs-vswitchd.
+    AT_CHECK([ovs-vswitchd $1 --detach --no-chdir --pidfile --log-file -vvconn -vofproto_dpif -vunixctl], [0], [], [stderr])
+-   AT_CAPTURE_FILE([ovs-vswitchd.log])
++   #AT_CAPTURE_FILE([ovs-vswitchd.log])
+    on_exit "kill_ovs_vswitchd `cat ovs-vswitchd.pid`"
+    AT_CHECK([[sed < stderr '
+ /ovs_numa|INFO|Discovered /d
 diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at
-index 73971b3f4..50f31b22f 100644
+index 73971b3f4..7f7d1cf40 100644
 --- a/tests/ovn-controller-vtep.at
 +++ b/tests/ovn-controller-vtep.at
+@@ -50,7 +50,7 @@ m4_define([OVN_CONTROLLER_VTEP_START], [
+ /vlog|INFO|opened log file/d'
+ 
+    dnl Wait until ovs-vtep starts up.
+-   OVS_WAIT_UNTIL([test -n "`vtep-ctl show | grep Physical_Port`"])
++   OVS_WAIT_UNTIL([test 2 -eq "`vtep-ctl show | grep -c Physical_Port`"])
+ 
+    dnl Start ovn-controller-vtep.
+    start_daemon ovn-controller-vtep --vtep-db=unix:"$ovs_dir"/db.sock \
 @@ -653,9 +653,6 @@ AT_CHECK([grep -c $northd_version vtep1/ovn-controller-vtep.log], [0], [1
  as northd
  OVS_APP_EXIT_AND_WAIT([ovn-northd])
@@ -4284,7 +6628,7 @@ index 73971b3f4..50f31b22f 100644
  check ovn-sbctl set Chassis vtep1 vtep_logical_switches=foo
  
 diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
-index 4212d601a..6f496d142 100644
+index 4212d601a..b2a38a969 100644
 --- a/tests/ovn-controller.at
 +++ b/tests/ovn-controller.at
 @@ -60,7 +60,6 @@ check_bridge_mappings () {
@@ -4295,7 +6639,21 @@ index 4212d601a..6f496d142 100644
  kill `cat ovn-nb/ovsdb-server.pid`
  
  # Initially there should be no patch ports.
-@@ -526,6 +525,10 @@ OVS_WAIT_UNTIL([
+@@ -214,6 +213,13 @@ if test X$HAVE_OPENSSL = Xyes; then
+     # TODO implement check for change of certificates in ovn-controller
+     # and remove this workaround.
+     ovs-vsctl set Open_vSwitch . external-ids:ovn-remote=unix:/dev/null
++    # Make sure that the ovn-remote change is handled by ovn-controller.
++    # Without this, ovn-controller could handle both this change and next ovn-remote change within the same loop,
++    # resulting in no change.
++    # Use 2 ovn-appctl to guarentee that ovn-controller run the full loop, and not just the unixctl handling
++    OVS_WAIT_UNTIL([test x$(ovn-appctl -t ovn-controller debug/status) = "xrunning"])
++    OVS_WAIT_UNTIL([test x$(ovn-appctl -t ovn-controller debug/status) = "xrunning"])
++
+ fi
+ ovs-vsctl -- set Open_vSwitch . external-ids:hostname="${sysid}" \
+           -- set Open_vSwitch . external-ids:system-id="${sysid}" \
+@@ -526,6 +532,10 @@ OVS_WAIT_UNTIL([
      test "$(ovn-sbctl get chassis hv1 other_config:port-up-notif)" = '"true"'
  ])
  
@@ -4306,7 +6664,7 @@ index 4212d601a..6f496d142 100644
  OVN_CLEANUP([hv1])
  AT_CLEANUP
  ])
-@@ -575,10 +578,8 @@ localport lport : [[lsp1]]
+@@ -575,10 +585,8 @@ localport lport : [[lsp1]]
  
  # pause ovn-northd
  check as northd ovn-appctl -t ovn-northd pause
@@ -4317,7 +6675,7 @@ index 4212d601a..6f496d142 100644
  
  pb_types=(patch chassisredirect l3gateway localnet localport l2gateway
            virtual external remote vtep)
-@@ -2093,7 +2094,6 @@ AT_CHECK([ovs-ofctl dump-flows br-int table=46 | grep -c "priority=1100"], [0],
+@@ -2093,7 +2101,6 @@ AT_CHECK([ovs-ofctl dump-flows br-int table=46 | grep -c "priority=1100"], [0],
  
  # pause ovn-northd
  check as northd ovn-appctl -t ovn-northd pause
@@ -4325,7 +6683,7 @@ index 4212d601a..6f496d142 100644
  
  # Simulate a SB address set "del and add" notification to ovn-controller in the
  # same IDL iteration. The flows programmed by ovn-controller should reflect the
-@@ -2118,7 +2118,7 @@ AT_CLEANUP
+@@ -2118,7 +2125,7 @@ AT_CLEANUP
  
  AT_SETUP([ovn-controller - I-P handle lb_hairpin_use_ct_mark change])
  
@@ -4334,29 +6692,55 @@ index 4212d601a..6f496d142 100644
  
  net_add n1
  sim_add hv1
-@@ -2202,6 +2202,10 @@ OVS_APP_EXIT_AND_WAIT([ovn-controller])
- # The old OVS flows should remain (this is regardless of the configuration)
- AT_CHECK([ovs-ofctl dump-flows br-int | grep 10.1.2.3], [0], [ignore])
+@@ -2193,14 +2200,18 @@ check ovn-nbctl --wait=hv sync
+ AT_CHECK([ovn-appctl -t ovn-controller group-table-list | awk '{print $2}' | sort | uniq | wc -l], [0], [2
+ ])
  
+-# Set 5 seconds wait time before clearing OVS flows.
+-check ovs-vsctl set open . external_ids:ovn-ofctrl-wait-before-clear=5000
+-
+ # Stop ovn-controller
+ OVS_APP_EXIT_AND_WAIT([ovn-controller])
+ 
++# Set 5 seconds wait time before clearing OVS flows.
++check ovs-vsctl set open . external_ids:ovn-ofctrl-wait-before-clear=5000
++
+ # The old OVS flows should remain (this is regardless of the configuration)
+-AT_CHECK([ovs-ofctl dump-flows br-int | grep 10.1.2.3], [0], [ignore])
++AT_CHECK([ovs-ofctl dump-flows br-int | grep -F 10.1.2.3], [0], [ignore])
++
 +# We should have 2 flows with groups.
 +AT_CHECK([ovs-ofctl dump-flows br-int | grep group -c], [0], [2
 +])
-+
+ 
  # Make a change to the ls1-lp1's IP
  check ovn-nbctl --wait=sb lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.1.2.4"
+@@ -2208,16 +2219,29 @@ check ovn-nbctl --wait=sb lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.1.2.4"
+ # Start ovn-controller, which should compute new flows but not apply them
+ # until the wait time is completed.
+ start_daemon ovn-controller
+-sleep 2
++
++# Wait for octrl to run - it will handle the wait-before-clear
++OVS_WAIT_UNTIL([grep -q 'wait-before-clear' hv1/ovn-controller.log])
++
++# Check that there is no flow using 10.1.2.4 except the lb one (using 2.2.2.2)
++OVS_WAIT_UNTIL([test 0 = $(ovs-ofctl dump-flows br-int | grep -F 10.1.2.4 | grep -cvF 2.2.2.2)])
  
-@@ -2214,10 +2218,18 @@ sleep 2
+ # Check in the middle of the wait.
  lflow_run_1=$(ovn-appctl -t ovn-controller coverage/read-counter lflow_run)
- AT_CHECK([ovs-ofctl dump-flows br-int | grep 10.1.2.3], [0], [ignore])
- 
+-AT_CHECK([ovs-ofctl dump-flows br-int | grep 10.1.2.3], [0], [ignore])
++AT_CHECK([ovs-ofctl dump-flows br-int | grep -F 10.1.2.3], [0], [ignore])
++
 +# We should have 2 flows with groups.
 +AT_CHECK([ovs-ofctl dump-flows br-int | grep group -c], [0], [2
 +])
-+
+ 
  sleep 5
  
  # Check after the wait
- OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 10.1.2.4])
+-OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 10.1.2.4])
++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep -F 10.1.2.4 | grep -vF 2.2.2.2])
 +
 +# We should have 2 flows with groups.
 +AT_CHECK([ovs-ofctl dump-flows br-int | grep group -c], [0], [2
@@ -4364,16 +6748,19 @@ index 4212d601a..6f496d142 100644
  lflow_run_2=$(ovn-appctl -t ovn-controller coverage/read-counter lflow_run)
  
  # Verify that the flow compute completed during the wait (after the wait it
-@@ -2230,7 +2242,7 @@ OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+@@ -2228,9 +2252,9 @@ AT_CHECK_UNQUOTED([echo $lflow_run_1], [0], [$lflow_run_2
+ # Restart OVS this time, and wait until flows are reinstalled
+ OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
  start_daemon ovs-vswitchd --enable-dummy=system -vvconn -vofproto_dpif -vunixctl
- OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 10.1.2.4])
+-OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 10.1.2.4])
++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep -F 10.1.2.4 | grep -vF 2.2.2.2])
  
 -check ovn-nbctl --wait=hv lb-add lb3 2.2.2.2 10.1.2.5 \
 +check ovn-nbctl --wait=hv lb-add lb3 3.3.3.3 10.1.2.5 \
  -- ls-lb-add ls1 lb3
  
  # There should be 3 group IDs allocated (this is to ensure the group ID
-@@ -2238,6 +2250,10 @@ check ovn-nbctl --wait=hv lb-add lb3 2.2.2.2 10.1.2.5 \
+@@ -2238,6 +2262,10 @@ check ovn-nbctl --wait=hv lb-add lb3 2.2.2.2 10.1.2.5 \
  AT_CHECK([ovn-appctl -t ovn-controller group-table-list | awk '{print $2}' | sort | uniq | wc -l], [0], [3
  ])
  
@@ -4384,11 +6771,67 @@ index 4212d601a..6f496d142 100644
  OVN_CLEANUP([hv1])
  AT_CLEANUP
  
+@@ -2651,3 +2679,23 @@ OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-flows br-int table=0 | grep -c in_por
+ OVN_CLEANUP([hv1])
+ AT_CLEANUP
+ ])
++
++AT_SETUP([ovn-controller - ssl ciphers using command line options])
++AT_KEYWORDS([ovn])
++AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
++ovn_start
++
++net_add n1
++sim_add hv1
++ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.20
++
++# Set cipher and and it should connect
++OVS_APP_EXIT_AND_WAIT([ovn-controller])
++start_daemon ovn-controller --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2'
++
++OVS_WAIT_FOR_OUTPUT([ovn-appctl -t ovn-controller connection-status], [0], [connected
++])
++
++OVN_CLEANUP([hv1])
++AT_CLEANUP
 diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
-index d265bb90b..d4c436f84 100644
+index d265bb90b..49e1e39d6 100644
 --- a/tests/ovn-ic.at
 +++ b/tests/ovn-ic.at
-@@ -92,6 +92,7 @@ check ovn-nbctl lr-add lr1
+@@ -28,7 +28,31 @@ availability-zone az3
+ ])
+ 
+ OVN_CLEANUP_IC([az1], [az2])
++AT_CLEANUP
++])
++
++
++OVN_FOR_EACH_NORTHD([
++AT_SETUP([ovn-ic -- AZ update in GW])
++ovn_init_ic_db
++net_add n1
++
++ovn_start az1
++sim_add gw-az1
++as gw-az1
++
++check ovs-vsctl add-br br-phys
++ovn_az_attach az1 n1 br-phys 192.168.1.1
++check ovs-vsctl set open . external-ids:ovn-is-interconn=true
+ 
++az_uuid=$(fetch_column ic-sb:availability-zone _uuid name="az1")
++ovn_as az1 ovn-nbctl set NB_Global . name="az2"
++wait_column "$az_uuid" ic-sb:availability-zone _uuid name="az2"
++
++# make sure that gateway still point to the same AZ with new name
++wait_column "$az_uuid" ic-sb:gateway availability_zone name="gw-az1"
++
++OVN_CLEANUP_IC([az1])
+ AT_CLEANUP
+ ])
+ 
+@@ -92,6 +116,7 @@ check ovn-nbctl lr-add lr1
  check ovn-nbctl lrp-add lr1 lrp1 00:00:00:00:00:01 10.0.0.1/24
  check ovn-nbctl lrp-set-gateway-chassis lrp1 gw-az1
  
@@ -4396,7 +6839,7 @@ index d265bb90b..d4c436f84 100644
  check ovn-nbctl lsp-add ts1 lsp1 -- \
      lsp-set-addresses lsp1 router -- \
      lsp-set-type lsp1 router -- \
-@@ -156,6 +157,7 @@ create_ic_infra() {
+@@ -156,6 +181,7 @@ create_ic_infra() {
      ovn_as $az
  
      check ovn-ic-nbctl ts-add $ts
@@ -4404,7 +6847,7 @@ index d265bb90b..d4c436f84 100644
      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 lrp-set-gateway-chassis $lrp gw-$az
-@@ -227,6 +229,7 @@ for i in 1 2; do
+@@ -227,6 +253,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
  
@@ -4412,7 +6855,7 @@ index d265bb90b..d4c436f84 100644
      check ovn-nbctl lsp-add ts1 lsp$i -- \
          lsp-set-addresses lsp$i router -- \
          lsp-set-type lsp$i router -- \
-@@ -290,7 +293,7 @@ ovs-vsctl set open . external-ids:ovn-is-interconn=true
+@@ -290,7 +317,7 @@ ovs-vsctl set open . external-ids:ovn-is-interconn=true
  OVS_WAIT_UNTIL([ovn_as az2 ovn-sbctl show | grep gw1])
  
  OVN_CLEANUP_SBOX(gw1)
@@ -4421,7 +6864,7 @@ index d265bb90b..d4c436f84 100644
  ])
  
  # Test encap change
-@@ -335,16 +338,17 @@ ovn-nbctl lsp-add ts1 lsp-ts1-lr1
+@@ -335,16 +362,17 @@ ovn-nbctl lsp-add ts1 lsp-ts1-lr1
  ovn-nbctl lsp-set-addresses lsp-ts1-lr1 router
  ovn-nbctl lsp-set-type lsp-ts1-lr1 router
  ovn-nbctl --wait=hv lsp-set-options lsp-ts1-lr1 router-port=lrp-lr1-ts1
@@ -4442,7 +6885,7 @@ index d265bb90b..d4c436f84 100644
  lsp-ts1-lr1,remote
  ])
  
-@@ -529,6 +533,7 @@ for i in 1 2; do
+@@ -529,6 +557,7 @@ for i in 1 2; do
      for j in 1 2; do
          ts=ts$j$j
          ovn-ic-nbctl --may-exist ts-add $ts
@@ -4450,7 +6893,7 @@ index d265bb90b..d4c436f84 100644
  
          # Create LRP and connect to TS
          lr=lr$j$i
-@@ -552,6 +557,9 @@ ovn_as az1 ovn-nbctl show
+@@ -552,6 +581,9 @@ ovn_as az1 ovn-nbctl show
  echo az2
  ovn_as az2 ovn-nbctl show
  
@@ -4460,7 +6903,7 @@ index d265bb90b..d4c436f84 100644
  # Test routes from lr12 were learned to lr11
  AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 |
               grep learned | awk '{print $1, $2}' | sort], [0], [dnl
-@@ -584,6 +592,7 @@ for i in 1 2; do
+@@ -584,6 +616,7 @@ for i in 1 2; do
      # Create LRP and connect to TS
      ovn-nbctl lr-add lr$i
      ovn-nbctl lrp-add lr$i lrp-lr$i-ts1 aa:aa:aa:aa:aa:0$i 169.254.100.$i/24
@@ -4468,7 +6911,7 @@ index d265bb90b..d4c436f84 100644
      ovn-nbctl lsp-add ts1 lsp-ts1-lr$i \
              -- lsp-set-addresses lsp-ts1-lr$i router \
              -- lsp-set-type lsp-ts1-lr$i router \
-@@ -909,6 +918,7 @@ for i in 1 2; do
+@@ -909,6 +942,7 @@ for i in 1 2; do
      for j in 1 2 3; do
          ts=ts1$j
          ovn-ic-nbctl --may-exist ts-add $ts
@@ -4476,7 +6919,7 @@ index d265bb90b..d4c436f84 100644
  
          lrp=lrp-$lr-$ts
          lsp=lsp-$ts-$lr
-@@ -934,6 +944,7 @@ for i in 1 2; do
+@@ -934,6 +968,7 @@ for i in 1 2; do
      for j in 1 2; do
          ts=ts2$j
          ovn-ic-nbctl --may-exist ts-add $ts
@@ -4484,7 +6927,7 @@ index d265bb90b..d4c436f84 100644
  
          lrp=lrp-$lr-$ts
          lsp=lsp-$ts-$lr
-@@ -957,7 +968,7 @@ ovn_as az2 ovn-nbctl --route-table=rtb3 lr-route-add lr12 10.10.10.0/24 192.168.
+@@ -957,7 +992,7 @@ ovn_as az2 ovn-nbctl --route-table=rtb3 lr-route-add lr12 10.10.10.0/24 192.168.
  ovn_as az2 ovn-nbctl --wait=sb lrp-add lr22 lrp-lr22 aa:aa:aa:aa:bb:01 "192.168.0.1/24"
  
  # Test direct routes from lr12 were learned to lr11
@@ -4493,7 +6936,7 @@ index d265bb90b..d4c436f84 100644
               grep learned | awk '{print $1, $2, $5}' | sort ], [0], [dnl
  192.168.0.0/24 169.254.101.2 ecmp
  192.168.0.0/24 169.254.102.2 ecmp
-@@ -965,24 +976,24 @@ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 192.168 |
+@@ -965,24 +1000,24 @@ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 192.168 |
  ])
  
  # Test static routes from lr12 rtbs rtb1,rtb2,rtb3 were learned to lr11
@@ -4522,7 +6965,7 @@ index d265bb90b..d4c436f84 100644
             192.168.0.0/24             169.254.101.2 dst-ip (learned) ecmp
             192.168.0.0/24             169.254.102.2 dst-ip (learned) ecmp
  ])
-@@ -1035,6 +1046,7 @@ for i in 1 2; do
+@@ -1035,6 +1070,7 @@ for i in 1 2; do
      for j in 1 2 3; do
          ts=ts1$j
          ovn-ic-nbctl --may-exist ts-add $ts
@@ -4530,7 +6973,7 @@ index d265bb90b..d4c436f84 100644
  
          lrp=lrp-$lr-$ts
          lsp=lsp-$ts-$lr
-@@ -1060,6 +1072,7 @@ for i in 1 2; do
+@@ -1060,6 +1096,7 @@ for i in 1 2; do
      for j in 1 2; do
          ts=ts2$j
          ovn-ic-nbctl --may-exist ts-add $ts
@@ -4538,7 +6981,7 @@ index d265bb90b..d4c436f84 100644
  
          lrp=lrp-$lr-$ts
          lsp=lsp-$ts-$lr
-@@ -1083,6 +1096,7 @@ ovn_as az2 ovn-nbctl --route-table=rtb3 lr-route-add lr12 2001:db8:aaaa::/64 200
+@@ -1083,6 +1120,7 @@ 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
@@ -4546,7 +6989,7 @@ index d265bb90b..d4c436f84 100644
  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
  2001:db8:200::/64 2001:db8:1::2 ecmp
-@@ -1091,16 +1105,19 @@ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 2001:db8:200 |
+@@ -1091,16 +1129,19 @@ AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr11 | grep 2001:db8:200 |
  ])
  
  # Test static routes from lr12 rtbs rtb1,rtb2,rtb3 were learned to lr11
@@ -4566,7 +7009,7 @@ index d265bb90b..d4c436f84 100644
  AT_CHECK([ovn_as az1 ovn-nbctl --route-table=rtb3 lr-route-list lr11], [0], [dnl
  IPv6 Routes
  Route Table rtb3:
-@@ -1108,6 +1125,7 @@ Route Table rtb3:
+@@ -1108,6 +1149,7 @@ Route Table rtb3:
  ])
  
  # Test routes from lr12 didn't leak as learned to lr21
@@ -4574,7 +7017,7 @@ index d265bb90b..d4c436f84 100644
  AT_CHECK([ovn_as az1 ovn-nbctl lr-route-list lr21 | grep 2001 | sort], [0], [dnl
          2001:db8:200::/64             2001:db8:1::2 dst-ip (learned) ecmp
          2001:db8:200::/64             2001:db8:2::2 dst-ip (learned) ecmp
-@@ -1127,6 +1145,7 @@ ovn-ic-nbctl ts-add ts1
+@@ -1127,6 +1169,7 @@ ovn-ic-nbctl ts-add ts1
  for i in 1 2; do
      ovn_start az$i
      ovn_as az$i
@@ -4582,7 +7025,7 @@ index d265bb90b..d4c436f84 100644
  
      # Enable route learning at AZ level
      ovn-nbctl set nb_global . options:ic-route-learn=true
-@@ -1152,13 +1171,13 @@ for i in 1 2; do
+@@ -1152,13 +1195,13 @@ 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
  
@@ -4598,7 +7041,7 @@ index d265bb90b..d4c436f84 100644
                  0.0.0.0/0              192.168.2.11 dst-ip
                10.0.0.0/24              192.168.2.10 dst-ip
             192.168.1.0/24             169.254.100.1 dst-ip (learned)
-@@ -1191,10 +1210,10 @@ done
+@@ -1191,10 +1234,10 @@ done
  # each LR has one connected subnet except TS port
  
  
@@ -4611,7 +7054,7 @@ index d265bb90b..d4c436f84 100644
  
  lr=lr11
  ovn-nbctl lr-add $lr
-@@ -1209,6 +1228,7 @@ ovn-nbctl lsp-add ts1 $lsp \
+@@ -1209,6 +1252,7 @@ ovn-nbctl lsp-add ts1 $lsp \
          -- lsp-set-options $lsp router-port=$lrp
  
  ovn_as az2
@@ -4619,7 +7062,7 @@ index d265bb90b..d4c436f84 100644
  for i in 1 2; do
      lr=lr2$i
      ovn-nbctl lr-add $lr
-@@ -1230,7 +1250,7 @@ ovn_as az2 ovn-nbctl lrp-add lr21 lrp-lr21 aa:aa:aa:aa:bc:01 "192.168.1.1/24"
+@@ -1230,7 +1274,7 @@ 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"
  
  # Test direct routes from lr21 and lr22 were learned to lr11
@@ -4628,6 +7071,109 @@ index d265bb90b..d4c436f84 100644
               grep learned | awk '{print $1, $2}' | sort ], [0], [dnl
  192.168.1.0/24 169.254.10.21
  192.168.2.0/24 169.254.10.22
+@@ -1254,3 +1298,102 @@ OVN_CLEANUP_IC([az1], [az2])
+ 
+ AT_CLEANUP
+ ])
++
++OVN_FOR_EACH_NORTHD([
++AT_SETUP([ovn-ic -- route sync -- IPv6 blacklist filter])
++AT_KEYWORDS([IPv6-route-sync-blacklist])
++
++ovn_init_ic_db
++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
++    check ovn-nbctl set nb_global . options:ic-route-adv=true
++    # Enable blacklist single filter for IPv6
++    check ovn-nbctl set nb_global . options:ic-route-blacklist=" \
++            2003:db8:1::/64,2004:aaaa::/32,2005:1234::/21"
++
++    OVS_WAIT_UNTIL([ovn-nbctl show | grep ts1])
++
++    # Create LRP and connect to TS
++    check ovn-nbctl lr-add lr$i
++    check ovn-nbctl lrp-add lr$i lrp-lr$i-ts1 aa:aa:aa:aa:aa:0$i \
++            2001:db8:1::$i/64
++    check ovn-nbctl lsp-add ts1 lsp-ts1-lr$i \
++            -- lsp-set-addresses lsp-ts1-lr$i router \
++            -- lsp-set-type lsp-ts1-lr$i router \
++            -- lsp-set-options lsp-ts1-lr$i router-port=lrp-lr$i-ts1
++
++    check ovn-nbctl lrp-add lr$i lrp-lr$i-p$i 00:00:00:00:00:0$i \
++            2002:db8:1::$i/64
++
++    # Create blacklisted LRPs and connect to TS
++    check ovn-nbctl lrp-add lr$i lrp-lr$i-p-ext$i \
++            11:11:11:11:11:1$i 2003:db8:1::$i/64
++
++    check ovn-nbctl lrp-add lr$i lrp-lr$i-p-ext2$i \
++            22:22:22:22:22:2$i 2004:aaaa:bbb::$i/48
++
++    # filtered by 2005:1234::/21 - (2005:1000: - 2005:17ff:)
++    check ovn-nbctl lrp-add lr$i lrp-lr$i-p-ext3$i \
++            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 \
++            44:44:44:44:44:4$i 2005:1834:5678::$i/50
++done
++
++for i in 1 2; do
++    OVS_WAIT_UNTIL([ovn_as az$i ovn-nbctl lr-route-list lr$i | grep learned])
++done
++
++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
++2005:1834:5678::/50 2001:db8:1::2
++])
++
++for i in 1 2; do
++    ovn_as az$i
++
++    # Drop blacklist
++    check ovn-nbctl remove nb_global . options ic-route-blacklist
++done
++
++OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr1 |
++    awk '/learned/{print $1, $2}' | sort ], [0], [dnl
++2002:db8:1::/64 2001:db8:1::2
++2003:db8:1::/64 2001:db8:1::2
++2004:aaaa:bbb::/48 2001:db8:1::2
++2005:1734:5678::/50 2001:db8:1::2
++2005:1834:5678::/50 2001:db8:1::2
++])
++
++for i in 1 2; do
++    ovn_as az$i
++
++    check ovn-nbctl set nb_global . \
++            options:ic-route-blacklist="2003:db8:1::/64,2004:db8:1::/64"
++
++    # Create an 'extra' blacklisted LRP and connect to TS
++    check ovn-nbctl lrp-add lr$i lrp-lr$i-p-ext5$i \
++            55:55:55:55:55:5$i 2004:db8:1::$i/64
++done
++
++OVS_WAIT_FOR_OUTPUT([ovn_as az1 ovn-nbctl lr-route-list lr1 |
++    awk '/learned/{print $1, $2}' | sort ], [0], [dnl
++2002:db8:1::/64 2001:db8:1::2
++2004:aaaa:bbb::/48 2001:db8:1::2
++2005:1734:5678::/50 2001:db8:1::2
++2005:1834:5678::/50 2001:db8:1::2
++])
++
++OVN_CLEANUP_IC([az1], [az2])
++
++AT_CLEANUP
++])
 diff --git a/tests/ovn-ipsec.at b/tests/ovn-ipsec.at
 index 10ef97878..f8df8d60e 100644
 --- a/tests/ovn-ipsec.at
@@ -4647,7 +7193,7 @@ index 10ef97878..f8df8d60e 100644
  AT_CHECK([as hv1 ovs-vsctl get Interface ovn-hv2-0 options:remote_name | tr -d '\n'], [0], [hv2])
  AT_CHECK([as hv1 ovs-vsctl get Interface ovn-hv2-0 options:ipsec_encapsulation | tr -d '\n'], [0], [yes])
 diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at
-index 13d5dc3d4..bb93fa5f0 100644
+index 13d5dc3d4..a5a3e87fd 100644
 --- a/tests/ovn-macros.at
 +++ b/tests/ovn-macros.at
 @@ -188,16 +188,16 @@ ovn_start_northd() {
@@ -4809,6 +7355,16 @@ index 13d5dc3d4..bb93fa5f0 100644
  OVS_END_SHELL_HELPERS
  
  m4_define([OVN_POPULATE_ARP], [AT_CHECK(ovn_populate_arp__, [0], [ignore])])
+@@ -913,3 +983,9 @@ m4_define([RUN_OVN_NBCTL], [
+     check ovn-nbctl ${command}
+     unset command
+ ])
++
++m4_define([OVN_CHECK_SCAPY_EDNS_CLIENT_SUBNET_SUPPORT],
++[
++    AT_SKIP_IF([test $HAVE_SCAPY = no])
++    AT_SKIP_IF([! echo "from scapy.layers.dns import EDNS0ClientSubnet" | python 2>&1 > /dev/null])
++])
 diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
 index fde3a28ee..94641f2f0 100644
 --- a/tests/ovn-nbctl.at
@@ -4825,7 +7381,7 @@ index fde3a28ee..94641f2f0 100644
  AT_CHECK([ovn-nbctl show], [0], [ignore])
  OVN_NBCTL_TEST_STOP "/terminating with signal 15/d"
 diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
-index 65d3f4b03..c32122025 100644
+index 65d3f4b03..7add1aa57 100644
 --- a/tests/ovn-northd.at
 +++ b/tests/ovn-northd.at
 @@ -20,6 +20,14 @@ m4_define([_DUMP_DB_TABLES], [
@@ -4915,7 +7471,52 @@ index 65d3f4b03..c32122025 100644
    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") && ip4.dst == $disallowed_range), action=(next;)
  ])
  
-@@ -2860,7 +2868,7 @@ lbg0_uuid=$(fetch_column sb:load_balancer _uuid name=lbg0)
+@@ -1554,9 +1562,6 @@ AT_CAPTURE_FILE([sbflows])
+ # dnat_and_snat or snat entry.
+ AT_CHECK([grep "lr_in_unsnat" sbflows | sort], [0], [dnl
+   table=4 (lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
+-  table=4 (lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 192.168.2.1 && tcp && tcp.dst == 8080), action=(next;)
+-  table=4 (lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 192.168.2.4 && udp && udp.dst == 8080), action=(next;)
+-  table=4 (lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 192.168.2.5 && tcp && tcp.dst == 8080), action=(next;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 192.168.2.1), action=(ct_snat;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 192.168.2.4), action=(ct_snat;)
+ ])
+@@ -1587,9 +1592,6 @@ AT_CAPTURE_FILE([sbflows])
+ # dnat_and_snat or snat entry.
+ AT_CHECK([grep "lr_in_unsnat" sbflows | sort], [0], [dnl
+   table=4 (lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
+-  table=4 (lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 192.168.2.1 && tcp && tcp.dst == 8080), action=(next;)
+-  table=4 (lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 192.168.2.4 && udp && udp.dst == 8080), action=(next;)
+-  table=4 (lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 192.168.2.5 && tcp && tcp.dst == 8080), action=(next;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 192.168.2.1), action=(ct_snat;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 192.168.2.4), action=(ct_snat;)
+ ])
+@@ -2376,6 +2378,24 @@ check_acl_lflow acl_two meter_me__${acl2} sw0
+ check_acl_lflow acl_three meter_me__${acl3} sw0
+ check_acl_lflow acl_three meter_me__${acl3} sw1
+ 
++AS_BOX([Disable/enable logging for acl3 while still referencing the meter])
++check_row_count meter 4
++check ovn-nbctl --wait=sb set acl $acl3 log=false
++check_row_count meter 3
++check_meter_by_name meter_me meter_me__${acl1} meter_me__${acl2}
++check_meter_by_name NOT meter_me__${acl3}
++check_acl_lflow acl_one meter_me__${acl1} sw0
++check_acl_lflow acl_two meter_me__${acl2} sw0
++
++check ovn-nbctl --wait=sb set acl $acl3 log=true
++check_row_count meter 4
++check_meter_by_name \
++    meter_me meter_me__${acl1} meter_me__${acl2} meter_me__${acl3}
++check_acl_lflow acl_one meter_me__${acl1} sw0
++check_acl_lflow acl_two meter_me__${acl2} sw0
++check_acl_lflow acl_three meter_me__${acl3} sw0
++check_acl_lflow acl_three meter_me__${acl3} sw1
++
+ AS_BOX([Stop using meter for acl1])
+ check ovn-nbctl --wait=sb clear acl $acl1 meter
+ check_meter_by_name meter_me meter_me__${acl2}
+@@ -2860,7 +2880,7 @@ lbg0_uuid=$(fetch_column sb:load_balancer _uuid name=lbg0)
  echo
  echo "__file__:__line__: Check that SB lb0 has sw0 in datapaths column."
  
@@ -4924,7 +7525,7 @@ index 65d3f4b03..c32122025 100644
  AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl
                      | grep -A1 $lb0_dp_group | tail -1], [0], [dnl
  $sw0_sb_uuid
-@@ -2871,7 +2879,7 @@ check_column "" sb:datapath_binding load_balancers external_ids:name=sw0
+@@ -2871,7 +2891,7 @@ check_column "" sb:datapath_binding load_balancers external_ids:name=sw0
  echo
  echo "__file__:__line__: Check that SB lbg0 has sw0 in datapaths column."
  
@@ -4933,7 +7534,7 @@ index 65d3f4b03..c32122025 100644
  AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl
                      | grep -A1 $lbg0_dp_group | tail -1], [0], [dnl
  $sw0_sb_uuid
-@@ -2897,6 +2905,15 @@ sb:load_balancer vips,protocol name=lbg0
+@@ -2897,6 +2917,15 @@ sb:load_balancer vips,protocol name=lbg0
  
  check ovn-nbctl lr-add lr0 -- add logical_router lr0 load_balancer_group $lbg
  check ovn-nbctl --wait=sb lr-lb-add lr0 lb0
@@ -4949,7 +7550,7 @@ index 65d3f4b03..c32122025 100644
  
  echo
  echo "__file__:__line__: Check that SB lb0 has only sw0 in datapaths column."
-@@ -2942,7 +2959,13 @@ check_row_count sb:load_balancer 2
+@@ -2942,7 +2971,13 @@ check_row_count sb:load_balancer 2
  lbg1=$(fetch_column nb:load_balancer _uuid name=lbg1)
  check ovn-nbctl add load_balancer_group $lbg load_balancer $lbg1
  check ovn-nbctl --wait=sb lr-lb-add lr0 lb1
@@ -4964,7 +7565,7 @@ index 65d3f4b03..c32122025 100644
  
  echo
  echo "__file__:__line__: Associate lb1 to sw1 and check that lb1 is created in SB DB."
-@@ -2959,7 +2982,7 @@ echo "__file__:__line__: Check that SB lbg1 has vips and protocol columns are se
+@@ -2959,7 +2994,7 @@ echo "__file__:__line__: Check that SB lbg1 has vips and protocol columns are se
  check_column "20.0.0.30:80=20.0.0.50:8080 udp" sb:load_balancer vips,protocol name=lbg1
  
  lb1_uuid=$(fetch_column sb:load_balancer _uuid name=lb1)
@@ -4973,7 +7574,7 @@ index 65d3f4b03..c32122025 100644
  
  echo
  echo "__file__:__line__: Check that SB lb1 has sw1 in datapaths column."
-@@ -2970,7 +2993,7 @@ $sw1_sb_uuid
+@@ -2970,7 +3005,7 @@ $sw1_sb_uuid
  ])
  
  lbg1_uuid=$(fetch_column sb:load_balancer _uuid name=lbg1)
@@ -4982,7 +7583,7 @@ index 65d3f4b03..c32122025 100644
  
  echo
  echo "__file__:__line__: Check that SB lbg1 has sw0 and sw1 in datapaths column."
-@@ -4596,7 +4619,7 @@ AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
+@@ -4596,7 +4631,7 @@ AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
  PKIDIR="$(cd $abs_top_builddir/tests && pwd)"
  AT_SKIP_IF([expr "$PKIDIR" : ".*[[ 	'\"
  \\]]"])
@@ -4991,7 +7592,7 @@ index 65d3f4b03..c32122025 100644
  
  as northd
  OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE])
-@@ -5095,6 +5118,7 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
+@@ -5095,6 +5130,7 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
@@ -4999,7 +7600,7 @@ index 65d3f4b03..c32122025 100644
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
  ])
-@@ -5109,6 +5133,7 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed 's/table=../table=??/' | sort],
+@@ -5109,6 +5145,7 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed 's/table=../table=??/' | sort],
    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:02:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.2.1), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.100), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
@@ -5007,7 +7608,7 @@ index 65d3f4b03..c32122025 100644
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:201), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
  ])
  
-@@ -5129,8 +5154,10 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
+@@ -5129,8 +5166,10 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
@@ -5018,7 +7619,7 @@ index 65d3f4b03..c32122025 100644
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
  ])
  
-@@ -5144,7 +5171,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed 's/table=../table=??/' | sort],
+@@ -5144,7 +5183,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed 's/table=../table=??/' | sort],
    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:02:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.2.1), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.100), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
@@ -5028,7 +7629,7 @@ index 65d3f4b03..c32122025 100644
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:201), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
  ])
  
-@@ -5162,9 +5191,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
+@@ -5162,9 +5203,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
@@ -5040,7 +7641,7 @@ index 65d3f4b03..c32122025 100644
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
  ])
  
-@@ -5180,9 +5211,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
+@@ -5180,9 +5223,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
@@ -5052,7 +7653,7 @@ index 65d3f4b03..c32122025 100644
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
  ])
  
-@@ -5204,9 +5237,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
+@@ -5204,9 +5249,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
@@ -5064,7 +7665,7 @@ index 65d3f4b03..c32122025 100644
    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
  ])
  
-@@ -5364,12 +5399,12 @@ AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' | sort],
+@@ -5364,12 +5411,12 @@ AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' | sort],
  AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
    table=? (lr_out_snat        ), priority=0    , match=(1), action=(next;)
    table=? (lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
@@ -5083,7 +7684,7 @@ index 65d3f4b03..c32122025 100644
  ])
  
  # Separate zones for DGP
-@@ -5412,9 +5447,9 @@ AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' | sort],
+@@ -5412,9 +5459,9 @@ AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' | sort],
  AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
    table=? (lr_out_snat        ), priority=0    , match=(1), action=(next;)
    table=? (lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
@@ -5096,7 +7697,7 @@ index 65d3f4b03..c32122025 100644
  ])
  
  # Associate load balancer to lr0
-@@ -5494,12 +5529,12 @@ AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' | sort],
+@@ -5494,12 +5541,12 @@ AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' | sort],
  AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
    table=? (lr_out_snat        ), priority=0    , match=(1), action=(next;)
    table=? (lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
@@ -5115,7 +7716,7 @@ index 65d3f4b03..c32122025 100644
  ])
  
  # Separate zones for DGP
-@@ -5560,9 +5595,9 @@ AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' | sort],
+@@ -5560,9 +5607,9 @@ AT_CHECK([grep "lr_out_post_undnat" lr0flows | sed 's/table=./table=?/' | sort],
  AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], [dnl
    table=? (lr_out_snat        ), priority=0    , match=(1), action=(next;)
    table=? (lr_out_snat        ), priority=120  , match=(nd_ns), action=(next;)
@@ -5128,7 +7729,23 @@ index 65d3f4b03..c32122025 100644
  ])
  
  # Make the logical router as Gateway router
-@@ -6019,9 +6054,9 @@ ovn_start
+@@ -5703,7 +5750,6 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], [dnl
+   table=4 (lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
+   table=4 (lr_in_unsnat       ), priority=110  , match=(inport == "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
+   table=4 (lr_in_unsnat       ), priority=110  , match=(inport == "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
+-  table=4 (lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 172.168.0.10 && tcp && tcp.dst == 9082), action=(next;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 172.168.0.10), action=(ct_snat;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 172.168.0.20), action=(ct_snat;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 172.168.0.30), action=(ct_snat;)
+@@ -5780,7 +5826,6 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], [dnl
+   table=4 (lr_in_unsnat       ), priority=110  , match=(inport == "lr0-public" && ip6.dst == def0::10), action=(ct_snat;)
+   table=4 (lr_in_unsnat       ), priority=110  , match=(inport == "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
+   table=4 (lr_in_unsnat       ), priority=110  , match=(inport == "lr0-sw0" && ip6.dst == aef0::1), action=(ct_snat;)
+-  table=4 (lr_in_unsnat       ), priority=120  , match=(ip4 && ip4.dst == 172.168.0.10 && tcp && tcp.dst == 9082), action=(next;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 172.168.0.10), action=(ct_snat;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 172.168.0.20), action=(ct_snat;)
+   table=4 (lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst == 172.168.0.30), action=(ct_snat;)
+@@ -6019,9 +6064,9 @@ ovn_start
  check ovn-nbctl ls-add ls -- lb-add lb1 10.0.0.1:80 10.0.0.2:80 -- ls-lb-add ls lb1
  check ovn-nbctl --wait=sb sync
  
@@ -5140,119 +7757,171 @@ index 65d3f4b03..c32122025 100644
  
  check ovn-nbctl --wait=sb sync
  check_row_count Load_Balancer 1
-@@ -6105,10 +6140,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
-   table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
-   table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1514); next;)
-   table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+@@ -6060,7 +6105,8 @@ OVN_FOR_EACH_NORTHD_NO_HV([
+ AT_SETUP([ovn -- gateway mtu check pkt larger flows])
+ ovn_start
+ 
+-check ovn-sbctl chassis-add ch1 geneve 127.0.0.1
++check ovn-sbctl chassis-add ch1 geneve 127.0.0.1 --\
++                set chassis ch1 other_config:ct-commit-nat-v2=true
+ 
+ check ovn-nbctl ls-add sw0
+ check ovn-nbctl ls-add sw1
+@@ -6109,6 +6155,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
  ])
  
  AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
-@@ -6136,10 +6171,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
-   table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
-   table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1514); next;)
-   table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+@@ -6123,6 +6173,10 @@ AT_CHECK([grep -E "lr_in_ip_input.*icmp6_error" lr0flows | sort], [0], [dnl
+   table=3 (lr_in_ip_input     ), priority=150  , match=(inport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+ ])
+ 
++AT_CHECK([grep -E "lr_out_post_snat.*ct_commit_nat" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
++  table=??(lr_out_post_snat   ), priority=100  , match=(icmp && flags.icmp_snat == 1), action=(ct_commit_nat(snat);)
++])
++
+ # Clear the gateway-chassis for lr0-public
+ check ovn-nbctl --wait=sb clear logical_router_port lr0-public gateway_chassis
+ 
+@@ -6140,6 +6194,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
  ])
  
  AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
-@@ -6165,10 +6200,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
-   table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1514); next;)
-   table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-public" && (tcp)), action=(next;)
-   table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+@@ -6154,6 +6212,10 @@ AT_CHECK([grep -E "lr_in_ip_input.*icmp6_error" lr0flows | sort], [0], [dnl
+   table=3 (lr_in_ip_input     ), priority=150  , match=(inport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+ ])
+ 
++AT_CHECK([grep -E "lr_out_post_snat.*ct_commit_nat" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
++  table=??(lr_out_post_snat   ), priority=100  , match=(icmp && flags.icmp_snat == 1), action=(ct_commit_nat(snat);)
++])
++
+ # Set gateway_mtu_bypass to avoid check_pkt_larger() for tcp on lr0-public.
+ check ovn-nbctl --wait=sb set logical_router_port lr0-public options:gateway_mtu_bypass=tcp
+ 
+@@ -6169,6 +6231,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
  ])
  
  AT_CHECK([grep "lr_in_admission" lr0flows | grep -e "check_pkt_larger" -e "tcp" | sort], [0], [dnl
-@@ -6190,14 +6225,14 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
-   table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1414); next;)
-   table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-public" && (tcp)), action=(next;)
-   table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+@@ -6198,6 +6264,14 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
  ])
  
  AT_CHECK([grep "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
-@@ -6229,14 +6264,14 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
-   table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-public" && (tcp)), action=(next;)
-   table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-sw0" && (tcp)), action=(next;)
-   table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+@@ -6216,6 +6290,10 @@ AT_CHECK([grep -E "lr_in_ip_input.*icmp6_error" lr0flows | sort], [0], [dnl
+   table=3 (lr_in_ip_input     ), priority=150  , match=(inport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+ ])
+ 
++AT_CHECK([grep -E "lr_out_post_snat.*ct_commit_nat" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
++  table=??(lr_out_post_snat   ), priority=100  , match=(icmp && flags.icmp_snat == 1), action=(ct_commit_nat(snat);)
++])
++
+ # Set gateway_mtu_bypass to avoid check_pkt_larger() for tcp on lr0-sw0.
+ check ovn-nbctl --wait=sb set logical_router_port lr0-sw0 options:gateway_mtu_bypass=tcp
+ 
+@@ -6237,6 +6315,14 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
  ])
  
  AT_CHECK([grep "lr_in_admission" lr0flows | grep -e "check_pkt_larger" -e "tcp" | sort], [0], [dnl
-@@ -6255,15 +6290,16 @@ check ovn-nbctl --wait=sb clear logical_router_port lr0-public options
- ovn-sbctl dump-flows lr0 > lr0flows
- AT_CAPTURE_FILE([lr0flows])
- 
-+grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=../table=??/' | sort
- AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
-   table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
-   table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1414); next;)
-   table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-sw0" && (tcp)), action=(next;)
-   table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
--  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
-+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+@@ -6264,6 +6350,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
++  table=??(lr_in_larger_pkts  ), priority=160  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp6_error {flags.icmp_snat = 1; reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
  ])
  
  check ovn-nbctl --wait=sb clear logical_router_port lr0-sw0 options
-@@ -7361,9 +7397,9 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat | sed 's/table=../table=??/'
+@@ -6296,6 +6386,12 @@ AT_CHECK([grep "lr_in_admission" lr0flows | grep -e "check_pkt_larger" | sort],
+   table=0 (lr_in_admission    ), priority=50   , match=(eth.mcast && inport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); xreg0[[0..47]] = 00:00:20:20:12:13; next;)
+ ])
+ 
++check ovn-sbctl set chassis ch1 other_config:ct-commit-nat-v2=false
++check ovn-nbctl --wait=sb sync
++
++ovn-sbctl dump-flows lr0 > lr0flows
++AT_CHECK([grep -E "lr_out_post_snat.*ct_commit_nat" lr0flows], [1])
++
+ AT_CLEANUP
+ ])
+ 
+@@ -6457,6 +6553,9 @@ AT_CAPTURE_FILE([lrflows])
+ 
+ # Check the flows in lr_in_admission stage
+ AT_CHECK([grep lr_in_admission lrflows | grep cr-DR | sort], [0], [dnl
++  table=0 (lr_in_admission    ), priority=120  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && eth.dst == 02:ac:10:01:00:01 && !is_chassis_resident("cr-DR-S1") && flags.tunnel_rx == 1), action=(outport <-> inport; inport = "DR-S1"; next;)
++  table=0 (lr_in_admission    ), priority=120  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && eth.dst == 03:ac:10:01:00:01 && !is_chassis_resident("cr-DR-S2") && flags.tunnel_rx == 1), action=(outport <-> inport; inport = "DR-S2"; next;)
++  table=0 (lr_in_admission    ), priority=120  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && eth.dst == 04:ac:10:01:00:01 && !is_chassis_resident("cr-DR-S3") && flags.tunnel_rx == 1), action=(outport <-> inport; inport = "DR-S3"; next;)
+   table=0 (lr_in_admission    ), priority=50   , match=(eth.dst == 02:ac:10:01:00:01 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")), action=(xreg0[[0..47]] = 02:ac:10:01:00:01; next;)
+   table=0 (lr_in_admission    ), priority=50   , match=(eth.dst == 03:ac:10:01:00:01 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2")), action=(xreg0[[0..47]] = 03:ac:10:01:00:01; next;)
+   table=0 (lr_in_admission    ), priority=50   , match=(eth.dst == 04:ac:10:01:00:01 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3")), action=(xreg0[[0..47]] = 04:ac:10:01:00:01; next;)
+@@ -6516,6 +6615,7 @@ AT_CAPTURE_FILE([lrflows])
+ 
+ # Check the flows in lr_in_admission stage
+ AT_CHECK([grep lr_in_admission lrflows | grep lrp1 | sed 's/table=../table=??/' | sort], [0], [dnl
++  table=??(lr_in_admission    ), priority=120  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && eth.dst == 00:00:00:00:00:01 && !is_chassis_resident("cr-lrp1") && flags.tunnel_rx == 1), action=(outport <-> inport; inport = "lrp1"; next;)
+   table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 00:00:00:00:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1")), action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+   table=??(lr_in_admission    ), priority=50   , match=(eth.mcast && inport == "lrp1"), action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+ ])
+@@ -6537,6 +6637,7 @@ AT_CAPTURE_FILE([lrflows])
+ 
+ # Check the flows in lr_in_admission stage
+ AT_CHECK([grep lr_in_admission lrflows | grep lrp1 | sed 's/table=../table=??/' | sort], [0], [dnl
++  table=??(lr_in_admission    ), priority=120  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && eth.dst == 00:00:00:00:00:01 && !is_chassis_resident("cr-lrp1") && flags.tunnel_rx == 1), action=(outport <-> inport; inport = "lrp1"; next;)
+   table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 00:00:00:00:00:01 && inport == "lrp1"), action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+   table=??(lr_in_admission    ), priority=50   , match=(eth.mcast && inport == "lrp1"), action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+ ])
+@@ -6555,6 +6656,7 @@ AT_CAPTURE_FILE([lrflows])
+ 
+ # Check the flows in lr_in_admission stage
+ AT_CHECK([grep lr_in_admission lrflows | grep lrp1 | sed 's/table=../table=??/' | sort], [0], [dnl
++  table=??(lr_in_admission    ), priority=120  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && eth.dst == 00:00:00:00:00:01 && !is_chassis_resident("cr-lrp1") && flags.tunnel_rx == 1), action=(outport <-> inport; inport = "lrp1"; next;)
+   table=??(lr_in_admission    ), priority=50   , match=(eth.dst == 00:00:00:00:00:01 && inport == "lrp1" && is_chassis_resident("cr-lrp1")), action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+   table=??(lr_in_admission    ), priority=50   , match=(eth.mcast && inport == "lrp1"), action=(xreg0[[0..47]] = 00:00:00:00:00:01; next;)
+ ])
+@@ -7361,9 +7463,9 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat | sed 's/table=../table=??/'
  ])
  
  AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | sed 's/table=../table=??/' | sort], [0], [dnl
@@ -5265,7 +7934,7 @@ index 65d3f4b03..c32122025 100644
  ])
  
  check ovn-nbctl --wait=sb lr-nat-del DR snat 20.0.0.10
-@@ -7437,9 +7473,9 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat | sed 's/table=../table=??/'
+@@ -7437,9 +7539,9 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat | sed 's/table=../table=??/'
  ])
  
  AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | sed 's/table=../table=??/' | sort], [0], [dnl
@@ -5278,7 +7947,55 @@ index 65d3f4b03..c32122025 100644
  ])
  
  AT_CHECK([grep lr_in_dnat lrflows | grep ct_dnat | sed 's/table=../table=??/' | sort], [0], [dnl
-@@ -8618,7 +8654,7 @@ AT_CHECK([ovn-sbctl dump-flows ls0 | grep -e 'ls_in_\(put\|lookup\)_fdb' | sort
+@@ -8283,6 +8385,7 @@ AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown |
+ sort | sed 's/table=../table=??/' ], [0], [dnl
+   table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
++  table=??(ls_in_check_port_sec), priority=110  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && flags.tunnel_rx == 1), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+   table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+   table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+@@ -8308,6 +8411,7 @@ AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown |
+ sort | sed 's/table=../table=??/' ], [0], [dnl
+   table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
++  table=??(ls_in_check_port_sec), priority=110  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && flags.tunnel_rx == 1), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+   table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+   table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+@@ -8334,6 +8438,7 @@ AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown |
+ sort | sed 's/table=../table=??/' ], [0], [dnl
+   table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
++  table=??(ls_in_check_port_sec), priority=110  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && flags.tunnel_rx == 1), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+   table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+   table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+@@ -8361,6 +8466,7 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
+   table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
+   table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
++  table=??(ls_in_check_port_sec), priority=110  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && flags.tunnel_rx == 1), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+   table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+   table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), action=(drop;)
+@@ -8387,6 +8493,7 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
+   table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=100  , match=(inport == "sw0p1"), action=(reg0[[15]] = 1; next;)
+   table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
++  table=??(ls_in_check_port_sec), priority=110  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && flags.tunnel_rx == 1), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+   table=??(ls_in_check_port_sec), priority=70   , match=(inport == "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
+   table=??(ls_in_apply_port_sec), priority=0    , match=(1), action=(next;)
+@@ -8416,6 +8523,7 @@ AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown |
+ sort | sed 's/table=../table=??/' ], [0], [dnl
+   table=??(ls_in_check_port_sec), priority=100  , match=(eth.src[[40]]), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=100  , match=(vlan.present), action=(drop;)
++  table=??(ls_in_check_port_sec), priority=110  , match=(((ip4 && icmp4.type == 3 && icmp4.code == 4) || (ip6 && icmp6.type == 2 && icmp6.code == 0)) && flags.tunnel_rx == 1), action=(drop;)
+   table=??(ls_in_check_port_sec), priority=50   , match=(1), action=(reg0[[15]] = check_in_port_sec(); next;)
+   table=??(ls_in_check_port_sec), priority=70   , match=(inport == "localnetport"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
+   table=??(ls_in_check_port_sec), priority=70   , match=(inport == "sw0p1"), action=(reg0[[14]] = 1; next(pipeline=ingress, table=17);)
+@@ -8618,7 +8726,7 @@ AT_CHECK([ovn-sbctl dump-flows ls0 | grep -e 'ls_in_\(put\|lookup\)_fdb' | sort
  AT_CHECK([ovn-nbctl --wait=sb lsp-set-options ln_port localnet_learn_fdb=true])
  AT_CHECK([ovn-sbctl dump-flows ls0 | grep -e 'ls_in_\(put\|lookup\)_fdb' | sort | sed 's/table=./table=?/'], [0], [dnl
    table=? (ls_in_lookup_fdb   ), priority=0    , match=(1), action=(next;)
@@ -5287,7 +8004,7 @@ index 65d3f4b03..c32122025 100644
    table=? (ls_in_put_fdb      ), priority=0    , match=(1), action=(next;)
    table=? (ls_in_put_fdb      ), priority=100  , match=(inport == "ln_port" && reg0[[11]] == 0), action=(put_fdb(inport, eth.src); next;)
  ])
-@@ -8701,7 +8737,7 @@ AT_CHECK([grep "ls_in_lb " S1flows | sed 's/table=../table=??/' | sort], [0], [d
+@@ -8701,7 +8809,7 @@ AT_CHECK([grep "ls_in_lb " S1flows | sed 's/table=../table=??/' | sort], [0], [d
  
  ovn-sbctl get datapath S0 _uuid > dp_uuids
  ovn-sbctl get datapath S1 _uuid >> dp_uuids
@@ -5296,7 +8013,97 @@ index 65d3f4b03..c32122025 100644
  AT_CHECK_UNQUOTED([ovn-sbctl --bare --columns _uuid,datapaths find Logical_DP_Group dnl
                      | grep -A1 $lb_dp_group | tail -1 | tr ' ' '\n' | sort], [0], [dnl
  $(cat dp_uuids | sort)
-@@ -8985,7 +9021,6 @@ grep -c mutate], [0], [2
+@@ -8827,8 +8935,8 @@ AT_CHECK([grep "ls_in_lb_aff_check" S0flows | sed 's/table=../table=??/' | sort]
+ AT_CHECK([grep "ls_in_lb " S0flows | sed 's/table=../table=??/' | sort], [0], [dnl
+   table=??(ls_in_lb           ), priority=0    , match=(1), action=(next;)
+   table=??(ls_in_lb           ), priority=120  , match=(ct.new && ip4.dst == 172.16.0.10 && tcp.dst == 80), action=(reg0[[1]] = 0; ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
+-  table=??(ls_in_lb           ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0[[1]] = 0; reg1 = 172.16.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.2:80);)
+-  table=??(ls_in_lb           ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0[[1]] = 0; reg1 = 172.16.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=20.0.0.2:80);)
++  table=??(ls_in_lb           ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0[[1]] = 0; reg1 = 172.16.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=10.0.0.2:80);)
++  table=??(ls_in_lb           ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0[[1]] = 0; reg1 = 172.16.0.10; reg2[[0..15]] = 80; ct_lb_mark(backends=20.0.0.2:80);)
+ ])
+ AT_CHECK([grep "ls_in_lb_aff_learn" S0flows | sed 's/table=../table=??/' | sort], [0], [dnl
+   table=??(ls_in_lb_aff_learn ), priority=0    , match=(1), action=(next;)
+@@ -8847,8 +8955,8 @@ AT_CHECK([grep "lr_in_lb_aff_check" R1flows | sed 's/table=../table=??/' | sort]
+ AT_CHECK([grep "lr_in_dnat " R1flows | sed 's/table=../table=??/' | sort], [0], [dnl
+   table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
+   table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
+-  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; ct_lb_mark(backends=10.0.0.2:80);)
+-  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; ct_lb_mark(backends=20.0.0.2:80);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; ct_lb_mark(backends=10.0.0.2:80);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; ct_lb_mark(backends=20.0.0.2:80);)
+   table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted), action=(next;)
+   table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && !ct.new), action=(ct_commit_nat;)
+   table=??(lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; next;)
+@@ -8871,8 +8979,8 @@ AT_CAPTURE_FILE([R1flows_skip_snat])
+ AT_CHECK([grep "lr_in_dnat " R1flows_skip_snat | sed 's/table=../table=??/' | sort], [0], [dnl
+   table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
+   table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80), action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80; skip_snat);)
+-  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; skip_snat);)
+-  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; skip_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; skip_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; skip_snat);)
+   table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted), action=(next;)
+   table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && !ct.new), action=(ct_commit_nat;)
+   table=??(lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; next;)
+@@ -8892,8 +9000,8 @@ AT_CAPTURE_FILE([R1flows_force_snat])
+ AT_CHECK([grep "lr_in_dnat " R1flows_force_snat | sed 's/table=../table=??/' | sort], [0], [dnl
+   table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
+   table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80), action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80; force_snat);)
+-  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; force_snat);)
+-  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.force_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; force_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; force_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.force_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; force_snat);)
+   table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted), action=(next;)
+   table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && !ct.new), action=(ct_commit_nat;)
+   table=??(lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; next;)
+@@ -8912,8 +9020,35 @@ AT_CAPTURE_FILE([R1flows_force_skip_snat])
+ AT_CHECK([grep "lr_in_dnat " R1flows_force_skip_snat | sed 's/table=../table=??/' | sort], [0], [dnl
+   table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
+   table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80), action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80; skip_snat);)
+-  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; skip_snat);)
+-  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; skip_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; skip_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; skip_snat);)
++  table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted), action=(next;)
++  table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && !ct.new), action=(ct_commit_nat;)
++  table=??(lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; next;)
++  table=??(lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; next;)
++  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && !ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; ct_commit_nat;)
++  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && !ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; ct_commit_nat;)
++])
++
++AS_BOX([Test LR flows - 2 LBs, lb0 skip_snat=true, lb1 lb_force_snat_ip="172.16.0.1"])
++check ovn-nbctl set logical_router R1 options:lb_force_snat_ip="172.16.0.1"
++check ovn-nbctl set load_balancer lb0 options:skip_snat=true
++check ovn-nbctl lb-add lb1 172.16.0.20:80 10.0.0.2:80,20.0.0.2:80 tcp
++check ovn-nbctl set load_balancer lb1 options:affinity_timeout=60
++check ovn-nbctl lr-lb-add R1 lb1
++check ovn-nbctl --wait=sb sync
++
++ovn-sbctl dump-flows R1 > R1flows_2lbs
++AT_CAPTURE_FILE([R1flows_2lbs])
++
++AT_CHECK([grep "lr_in_dnat " R1flows_2lbs | sed 's/table=../table=??/' | sort], [0], [dnl
++  table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
++  table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80), action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80; skip_snat);)
++  table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && ip4.dst == 172.16.0.20 && tcp && tcp.dst == 80), action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80; force_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; skip_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; skip_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.20 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.20; flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; force_snat);)
++  table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && ct.new && ip4.dst == 172.16.0.20 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 172.16.0.20; flags.force_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; force_snat);)
+   table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted), action=(next;)
+   table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && !ct.new), action=(ct_commit_nat;)
+   table=??(lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && !ct.new && ct_mark.natted && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; next;)
+@@ -8922,6 +9057,7 @@ AT_CHECK([grep "lr_in_dnat " R1flows_force_skip_snat | sed 's/table=../table=??/
+   table=??(lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && !ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; ct_commit_nat;)
+ ])
+ 
++
+ AT_CLEANUP
+ ])
+ 
+@@ -8985,7 +9121,6 @@ grep -c mutate], [0], [2
  # Pause ovn-northd and add/remove few addresses.  when it is resumed
  # it should use mutate for updating the address sets.
  check as northd ovn-appctl -t NORTHD_TYPE pause
@@ -5304,7 +8111,7 @@ index 65d3f4b03..c32122025 100644
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
  check ovn-nbctl add address_set $foo_as_uuid addresses 1.1.1.5
-@@ -10008,14 +10043,21 @@ ovs-vsctl add-br br-phys
+@@ -10008,14 +10143,21 @@ ovs-vsctl add-br br-phys
  ovn_attach n1 br-phys 192.168.0.11
  
  check_recompute_counter() {
@@ -5329,7 +8136,7 @@ index 65d3f4b03..c32122025 100644
  }
  
  check ovn-nbctl --wait=hv ls-add ls0
-@@ -10032,29 +10074,29 @@ check ovn-nbctl --wait=hv lsp-add ls0 lsp0-0 -- lsp-set-addresses lsp0-0 "unknow
+@@ -10032,29 +10174,29 @@ check ovn-nbctl --wait=hv lsp-add ls0 lsp0-0 -- lsp-set-addresses lsp0-0 "unknow
  ovs-vsctl add-port br-int lsp0-0 -- set interface lsp0-0 external_ids:iface-id=lsp0-0
  wait_for_ports_up
  check ovn-nbctl --wait=hv sync
@@ -5364,7 +8171,7 @@ index 65d3f4b03..c32122025 100644
  
  # Delete and re-add a LSP for several times continuously, to ensure
  # frequent operations do not trigger recompute when there are in-flight
-@@ -10068,14 +10110,14 @@ for i in $(seq 10); do
+@@ -10068,14 +10210,14 @@ for i in $(seq 10); do
      check ovn-nbctl lsp-del lsp0-2
      check ovn-nbctl lsp-add ls0 lsp0-2 -- lsp-set-addresses lsp0-2 "aa:aa:aa:00:00:02 192.168.0.12"
  done
@@ -5382,7 +8189,7 @@ index 65d3f4b03..c32122025 100644
  
  # Associate DHCP for lsp0-2
  ovn-nbctl dhcp-options-create 192.168.0.0/24
-@@ -10085,9 +10127,9 @@ ovn-nbctl dhcp-options-set-options $CIDR_UUID   lease_time=3600  router=192.168.
+@@ -10085,9 +10227,9 @@ ovn-nbctl dhcp-options-set-options $CIDR_UUID   lease_time=3600  router=192.168.
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
  ovn-nbctl --wait=sb lsp-set-dhcpv4-options lsp0-2 $CIDR_UUID
@@ -5394,7 +8201,7 @@ index 65d3f4b03..c32122025 100644
  
  # Add IPv6 address and associate DHCPv6 for lsp0-2
  check ovn-nbctl lsp-set-addresses lsp0-2 "aa:aa:aa:00:00:01 192.168.0.11 aef0::4"
-@@ -10096,9 +10138,9 @@ options="\"server_id\"=\"00:00:00:10:00:01\"")"
+@@ -10096,9 +10238,9 @@ options="\"server_id\"=\"00:00:00:10:00:01\"")"
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
  ovn-nbctl --wait=sb lsp-set-dhcpv6-options lsp0-2 ${d1}
@@ -5406,7 +8213,7 @@ index 65d3f4b03..c32122025 100644
  
  check ovn-nbctl --wait=hv ls-del ls0
  
-@@ -10125,11 +10167,11 @@ ovn-nbctl lsp-add ls0 ls0-lr0
+@@ -10125,11 +10267,11 @@ ovn-nbctl lsp-add ls0 ls0-lr0
  ovn-nbctl lsp-set-type ls0-lr0 router
  ovn-nbctl lsp-set-addresses ls0-lr0 router
  check ovn-nbctl --wait=sb lsp-set-options ls0-lr0 router-port=lr0-ls0
@@ -5420,7 +8227,7 @@ index 65d3f4b03..c32122025 100644
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
  # Add a lsp.  northd and lflow engine shouldn't recompute even though this is
-@@ -10216,14 +10258,14 @@ check ovn-nbctl --wait=sb meter-add m drop 1 pktps
+@@ -10216,14 +10358,14 @@ check ovn-nbctl --wait=sb meter-add m drop 1 pktps
  check ovn-nbctl --wait=sb acl-add ls from-lport 1 1 allow
  dnl Only triggers recompute of the sync_meters and lflow nodes.
  check_recompute_counter 0 2 2
@@ -5437,7 +8244,7 @@ index 65d3f4b03..c32122025 100644
  
  AT_CLEANUP
  ])
-@@ -10339,7 +10381,7 @@ wait_for_ports_up sw0-r1
+@@ -10339,7 +10481,7 @@ wait_for_ports_up sw0-r1
  check_column $remote_chassis_uuid Port_Binding chassis logical_port=sw0-r1
  
  # Set the type to router and ovn-northd should not claim it.
@@ -5446,7 +8253,7 @@ index 65d3f4b03..c32122025 100644
  check_column '' Port_Binding chassis logical_port=sw0-r1
  
  AT_CLEANUP
-@@ -10391,34 +10433,39 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+@@ -10391,34 +10533,39 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5489,7 +8296,7 @@ index 65d3f4b03..c32122025 100644
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
  
  AT_CHECK([ovn-nbctl --wait=sb \
-@@ -10429,6 +10476,7 @@ AT_CHECK([ovn-nbctl --wait=sb \
+@@ -10429,6 +10576,7 @@ AT_CHECK([ovn-nbctl --wait=sb \
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
@@ -5497,7 +8304,7 @@ index 65d3f4b03..c32122025 100644
  
  # Any change to load balancer health check should also result in full recompute
  # of northd node (but not northd_lb_data node)
-@@ -10437,6 +10485,7 @@ check ovn-nbctl --wait=sb set load_balancer_health_check . options:foo=bar1
+@@ -10437,6 +10585,7 @@ check ovn-nbctl --wait=sb set load_balancer_health_check . options:foo=bar1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
@@ -5505,7 +8312,7 @@ index 65d3f4b03..c32122025 100644
  
  # Delete the health check from the load balancer.  northd engine node should do a full recompute.
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10444,6 +10493,7 @@ check ovn-nbctl --wait=sb clear Load_Balancer . health_check
+@@ -10444,6 +10593,7 @@ check ovn-nbctl --wait=sb clear Load_Balancer . health_check
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
@@ -5513,7 +8320,7 @@ index 65d3f4b03..c32122025 100644
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
  check ovn-nbctl ls-add sw0
-@@ -10457,6 +10507,7 @@ ovn-nbctl --wait=sb lsp-set-options sw0-lr0 router-port=lr0-sw0
+@@ -10457,6 +10607,7 @@ ovn-nbctl --wait=sb lsp-set-options sw0-lr0 router-port=lr0-sw0
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
@@ -5521,7 +8328,7 @@ index 65d3f4b03..c32122025 100644
  
  # Associate lb1 to sw0. There should be no recompute of northd engine node
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10464,7 +10515,11 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
+@@ -10464,7 +10615,11 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5534,7 +8341,7 @@ index 65d3f4b03..c32122025 100644
  
  # Modify the backend of the lb1 vip
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10472,7 +10527,8 @@ check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.1
+@@ -10472,7 +10627,8 @@ check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5544,7 +8351,7 @@ index 65d3f4b03..c32122025 100644
  
  # Cleanup the vip of lb1.
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10480,7 +10536,8 @@ check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
+@@ -10480,7 +10636,8 @@ check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5554,7 +8361,7 @@ index 65d3f4b03..c32122025 100644
  
  # Set the vips of lb1 back
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10488,7 +10545,8 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+@@ -10488,7 +10645,8 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5564,7 +8371,7 @@ index 65d3f4b03..c32122025 100644
  
  # Add another vip to lb1
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10496,7 +10554,8 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
+@@ -10496,7 +10654,8 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5574,7 +8381,7 @@ index 65d3f4b03..c32122025 100644
  
  # Disassociate lb1 from sw0. There should be a full recompute of northd engine node.
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10504,7 +10563,8 @@ check ovn-nbctl --wait=sb ls-lb-del sw0 lb1
+@@ -10504,7 +10663,8 @@ check ovn-nbctl --wait=sb ls-lb-del sw0 lb1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
@@ -5584,7 +8391,7 @@ index 65d3f4b03..c32122025 100644
  
  # Associate lb1 to sw0 and also create a port sw0p1.  This should not result in
  # full recompute of northd, but should rsult in full recompute of lflow node.
-@@ -10513,6 +10573,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1 -- lsp-add sw0 sw0p1
+@@ -10513,6 +10673,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1 -- lsp-add sw0 sw0p1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5592,7 +8399,7 @@ index 65d3f4b03..c32122025 100644
  
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10523,6 +10584,7 @@ check ovn-nbctl --wait=sb ls-lb-del sw0 lb1
+@@ -10523,6 +10684,7 @@ check ovn-nbctl --wait=sb ls-lb-del sw0 lb1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
@@ -5600,7 +8407,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Add lb1 to lr0 and then disassociate
-@@ -10531,6 +10593,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr0 lb1
+@@ -10531,6 +10693,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr0 lb1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5608,7 +8415,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Modify the backend of the lb1 vip
-@@ -10539,6 +10602,7 @@ check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.1
+@@ -10539,6 +10702,7 @@ check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5616,7 +8423,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Cleanup the vip of lb1.
-@@ -10547,6 +10611,7 @@ check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
+@@ -10547,6 +10711,7 @@ check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5624,7 +8431,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Set the vips of lb1 back
-@@ -10555,6 +10620,7 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+@@ -10555,6 +10720,7 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5632,7 +8439,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Add another vip to lb1
-@@ -10563,6 +10629,7 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
+@@ -10563,6 +10729,7 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5640,7 +8447,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10570,14 +10637,16 @@ check ovn-nbctl --wait=sb lr-lb-del lr0 lb1
+@@ -10570,14 +10737,16 @@ check ovn-nbctl --wait=sb lr-lb-del lr0 lb1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
@@ -5658,7 +8465,7 @@ index 65d3f4b03..c32122025 100644
  
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10586,31 +10655,35 @@ lb1_uuid=$(fetch_column nb:Load_Balancer _uuid)
+@@ -10586,31 +10755,35 @@ lb1_uuid=$(fetch_column nb:Load_Balancer _uuid)
  
  # Add lb to the lbg1 group
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
@@ -5698,7 +8505,7 @@ index 65d3f4b03..c32122025 100644
  
  # Update lb and this should not result in northd recompute
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10618,6 +10691,7 @@ check ovn-nbctl --wait=sb set load_balancer . options:bar=foo
+@@ -10618,6 +10791,7 @@ check ovn-nbctl --wait=sb set load_balancer . options:bar=foo
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5706,7 +8513,7 @@ index 65d3f4b03..c32122025 100644
  
  # Modify the backend of the lb1 vip
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10625,6 +10699,7 @@ check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.1
+@@ -10625,6 +10799,7 @@ check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5714,7 +8521,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Cleanup the vip of lb1.
-@@ -10633,6 +10708,7 @@ check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
+@@ -10633,6 +10808,7 @@ check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5722,7 +8529,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Set the vips of lb1 back
-@@ -10641,6 +10717,7 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+@@ -10641,6 +10817,7 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5730,7 +8537,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Add another vip to lb1
-@@ -10649,19 +10726,22 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
+@@ -10649,19 +10826,22 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5754,7 +8561,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Modify the backend of the lb1 vip
-@@ -10670,6 +10750,7 @@ check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.1
+@@ -10670,6 +10850,7 @@ check ovn-nbctl --wait=sb set load_balancer lb1 vips:'"10.0.0.10:80"'='"10.0.0.1
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5762,7 +8569,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Cleanup the vip of lb1.
-@@ -10678,6 +10759,7 @@ check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
+@@ -10678,6 +10859,7 @@ check ovn-nbctl --wait=sb clear load_Balancer lb1 vips
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5770,7 +8577,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Set the vips of lb1 back
-@@ -10686,6 +10768,7 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
+@@ -10686,6 +10868,7 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.10:80 10.0.0.3:80
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5778,7 +8585,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Add another vip to lb1
-@@ -10694,27 +10777,31 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
+@@ -10694,27 +10877,31 @@ check ovn-nbctl --wait=sb lb-add lb1 10.0.0.20:80 10.0.0.30:8080
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5813,7 +8620,7 @@ index 65d3f4b03..c32122025 100644
  
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
-@@ -10733,29 +10820,33 @@ lb3_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb3)
+@@ -10733,29 +10920,33 @@ lb3_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb3)
  lb4_uuid=$(fetch_column nb:Load_Balancer _uuid name=lb4)
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
@@ -5851,7 +8658,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10763,6 +10854,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb2
+@@ -10763,6 +10954,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb2
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5859,7 +8666,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10770,6 +10862,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb3
+@@ -10770,6 +10962,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb3
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5867,7 +8674,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10778,6 +10871,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr1 lb2
+@@ -10778,6 +10971,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr1 lb2
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5875,7 +8682,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10785,6 +10879,7 @@ check ovn-nbctl --wait=sb ls-lb-del sw0 lb2
+@@ -10785,6 +10979,7 @@ check ovn-nbctl --wait=sb ls-lb-del sw0 lb2
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
@@ -5883,7 +8690,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10792,6 +10887,7 @@ check ovn-nbctl --wait=sb lr-lb-del lr1 lb2
+@@ -10792,6 +10987,7 @@ check ovn-nbctl --wait=sb lr-lb-del lr1 lb2
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
@@ -5891,7 +8698,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Deleting lb4 should not result in lflow recompute as it is
-@@ -10801,6 +10897,7 @@ check ovn-nbctl --wait=sb lb-del lb4
+@@ -10801,6 +10997,7 @@ check ovn-nbctl --wait=sb lb-del lb4
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5899,7 +8706,7 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  # Deleting lb2 should result in lflow recompute as it is
-@@ -10810,6 +10907,7 @@ check ovn-nbctl --wait=sb lb-del lb2
+@@ -10810,6 +11007,7 @@ check ovn-nbctl --wait=sb lb-del lb2
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd norecompute compute
  check_engine_stats lflow recompute nocompute
@@ -5907,15 +8714,15 @@ index 65d3f4b03..c32122025 100644
  CHECK_NO_CHANGE_AFTER_RECOMPUTE
  
  check as northd ovn-appctl -t NORTHD_TYPE inc-engine/clear-stats
-@@ -10817,7 +10915,61 @@ check ovn-nbctl --wait=sb remove load_balancer_group . load_balancer $lb3_uuid
+@@ -10817,7 +11015,61 @@ check ovn-nbctl --wait=sb remove load_balancer_group . load_balancer $lb3_uuid
  check_engine_stats lb_data norecompute compute
  check_engine_stats northd recompute nocompute
  check_engine_stats lflow recompute nocompute
 +check_engine_stats sync_to_sb_lb recompute compute
-+CHECK_NO_CHANGE_AFTER_RECOMPUTE
-+
-+AT_CLEANUP
-+])
+ CHECK_NO_CHANGE_AFTER_RECOMPUTE
+ 
+ AT_CLEANUP
+ ])
 +
 +OVN_FOR_EACH_NORTHD_NO_HV([
 +AT_SETUP([Load balancer incremental processing - batched updates])
@@ -5945,8 +8752,8 @@ index 65d3f4b03..c32122025 100644
 +check ovn-nbctl add load_balancer_group $lbg_uuid load_balancer $lb1_uuid
 +
 +check ovn-nbctl --wait=sb sync
- CHECK_NO_CHANGE_AFTER_RECOMPUTE
- 
++CHECK_NO_CHANGE_AFTER_RECOMPUTE
++
 +# Re-check the same scenario but now also batch the additional LB creation.
 +sleep_northd
 +check ovn-nbctl lb-add lb-temp 50.0.0.10:80 50.0.0.20:8080
@@ -5967,10 +8774,10 @@ index 65d3f4b03..c32122025 100644
 +Status: active
 +])
 +
- AT_CLEANUP
- ])
++AT_CLEANUP
++])
 diff --git a/tests/ovn.at b/tests/ovn.at
-index e127530f6..13f5575be 100644
+index e127530f6..07cc0e515 100644
 --- a/tests/ovn.at
 +++ b/tests/ovn.at
 @@ -23,8 +23,11 @@ m4_divert_text([PREPARE_TESTS],
@@ -6017,7 +8824,7 @@ index e127530f6..13f5575be 100644
 +     if [[ -n "$4" ]]; then
 +       cmd=$4
 +     else
-+       cmd=:
++       cmd=cat
 +     fi
       OVS_WAIT_UNTIL(
         [$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" $rcv_pcap > $rcv_text
@@ -6040,7 +8847,83 @@ index e127530f6..13f5575be 100644
     AT_CHECK([sort $rcv_text], [0], [expout], [ignore], [dump_diff__ "$1" "$2"])])
  
  m4_define([OVN_CHECK_PACKETS_REMOVE_BROADCAST],
-@@ -2198,13 +2219,13 @@ reg9[5] = chk_ecmp_nh();
+@@ -148,7 +169,7 @@ m4_define([OVN_CHECK_PACKETS_REMOVE_BROADCAST],
+    AT_CHECK([sort $rcv_text], [0], [expout], [ignore], [dump_diff__ "$1" "$2"])])
+ 
+ m4_define([OVN_CHECK_PACKETS_CONTAIN],
+-  [ovn_wait_packets__ "$1" "$2" "__file__:__line__"])
++  [ovn_wait_packets__ "$1" "$2" "__file__:__line__" $3])
+ 
+ # OVN_CHECK_PACKETS_UNIQ succeeds if some expected packets are duplicated.
+ # It fails if unexpected packets are received.
+@@ -1347,7 +1368,7 @@ ct_dnat(fd11::2);
+     has prereqs ip
+ ct_dnat(192.168.1.2, 1-3000);
+     formats as ct_dnat(192.168.1.2,1-3000);
+-    encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=192.168.1.2:1-3000))
++    encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=192.168.1.2:1-3000,random))
+     has prereqs ip
+ 
+ ct_dnat(192.168.1.2, 192.168.1.3);
+@@ -1381,7 +1402,7 @@ ct_dnat_in_czone(fd11::2);
+     has prereqs ip
+ ct_dnat_in_czone(192.168.1.2, 1-3000);
+     formats as ct_dnat_in_czone(192.168.1.2,1-3000);
+-    encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=192.168.1.2:1-3000))
++    encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=192.168.1.2:1-3000,random))
+     has prereqs ip
+ 
+ ct_dnat_in_czone(192.168.1.2, 192.168.1.3);
+@@ -1415,7 +1436,7 @@ ct_snat(fd11::2);
+     has prereqs ip
+ ct_snat(192.168.1.2, 1-3000);
+     formats as ct_snat(192.168.1.2,1-3000);
+-    encodes as ct(commit,table=19,zone=NXM_NX_REG12[0..15],nat(src=192.168.1.2:1-3000))
++    encodes as ct(commit,table=19,zone=NXM_NX_REG12[0..15],nat(src=192.168.1.2:1-3000,random))
+     has prereqs ip
+ 
+ ct_snat(192.168.1.2, 192.168.1.3);
+@@ -1449,7 +1470,7 @@ ct_snat_in_czone(fd11::2);
+     has prereqs ip
+ ct_snat_in_czone(192.168.1.2, 1-3000);
+     formats as ct_snat_in_czone(192.168.1.2,1-3000);
+-    encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(src=192.168.1.2:1-3000))
++    encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(src=192.168.1.2:1-3000,random))
+     has prereqs ip
+ 
+ ct_snat_in_czone(192.168.1.2, 192.168.1.3);
+@@ -1477,9 +1498,30 @@ ct_clear;
+ 
+ # ct_commit_nat
+ ct_commit_nat;
++    formats as ct_commit_nat(dnat);
++    encodes as ct(commit,table=19,zone=NXM_NX_REG13[0..15],nat)
++    has prereqs ip
++
++ct_commit_nat(snat);
++    encodes as ct(commit,table=19,zone=NXM_NX_REG13[0..15],nat)
++    has prereqs ip
++
++ct_commit_nat(dnat);
+     encodes as ct(commit,table=19,zone=NXM_NX_REG13[0..15],nat)
+     has prereqs ip
+ 
++ct_commit_nat(snat, dnat);
++    Syntax error at `,' expecting `)'.
++
++ct_commit_nat(dnat, ignore);
++    Syntax error at `,' expecting `)'.
++
++ct_commit_nat(ignore);
++    "ct_commit_nat" action accepts only "dnat" or "snat" parameter.
++
++ct_commit_nat();
++    "ct_commit_nat" action accepts only "dnat" or "snat" parameter.
++
+ # clone
+ clone { ip4.dst = 255.255.255.255; output; }; next;
+     encodes as clone(set_field:255.255.255.255->ip_dst,resubmit(,64)),resubmit(,19)
+@@ -2198,13 +2240,13 @@ reg9[5] = chk_ecmp_nh();
  
  # commit_lb_aff
  commit_lb_aff(vip = "172.16.0.123:8080", backend = "10.0.0.3:8080", proto = tcp, timeout = 30);
@@ -6057,7 +8940,7 @@ index e127530f6..13f5575be 100644
  
  # chk_lb_aff()
  reg9[6] = chk_lb_aff();
-@@ -3849,7 +3870,7 @@ OVN_FOR_EACH_NORTHD([
+@@ -3849,7 +3891,7 @@ OVN_FOR_EACH_NORTHD([
  AT_SETUP([VLAN transparency, passthru=true, multiple hosts, custom ethtype])
  ovn_start
  
@@ -6066,7 +8949,7 @@ index e127530f6..13f5575be 100644
  
  check ovn-nbctl ls-add ls
  check ovn-nbctl --wait=sb add Logical-Switch ls other_config vlan-passthru=true
-@@ -4650,6 +4671,12 @@ OVN_POPULATE_ARP
+@@ -4650,6 +4692,12 @@ OVN_POPULATE_ARP
  
  wait_for_ports_up
  check ovn-nbctl --wait=hv sync
@@ -6079,7 +8962,7 @@ index e127530f6..13f5575be 100644
  
  # test_packet INPORT DST SRC ETHTYPE OUTPORT...
  #
-@@ -4849,6 +4876,13 @@ check ovn-nbctl --wait=hv sync
+@@ -4849,6 +4897,13 @@ check ovn-nbctl --wait=hv sync
  # for ARP resolution).
  OVN_POPULATE_ARP
  
@@ -6093,7 +8976,7 @@ index e127530f6..13f5575be 100644
  # test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
  #
  # This shell function causes a packet to be received on INPORT.  The packet's
-@@ -5320,10 +5354,11 @@ test_arp() {
+@@ -5320,10 +5375,11 @@ test_arp() {
  }
  
  test_na() {
@@ -6108,7 +8991,7 @@ index e127530f6..13f5575be 100644
  
      hv=hv`vif_to_hv $inport`
      as $hv ovs-appctl netdev-dummy/receive vif$inport $request
-@@ -5399,6 +5434,24 @@ for i in 1 2; do
+@@ -5399,6 +5455,24 @@ for i in 1 2; do
      done
  done
  
@@ -6133,7 +9016,25 @@ index e127530f6..13f5575be 100644
  # Gracefully terminate daemons
  OVN_CLEANUP([hv1], [hv2])
  
-@@ -6758,13 +6811,7 @@ test_dhcp() {
+@@ -6107,7 +6181,7 @@ ovs-vsctl -- add-port br-int vif2 -- \
+ wait_for_ports_up
+ check ovn-nbctl --wait=hv sync
+ 
+-ovs-sbctl dump-flows > sbflows
++ovn-sbctl dump-flows > sbflows
+ AT_CAPTURE_FILE([sbflows])
+ 
+ # Send ip packets between the two ports.
+@@ -6123,7 +6197,7 @@ as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
+ #Disable router R1
+ ovn-nbctl --wait=hv set Logical_Router R1 enabled=false
+ 
+-ovs-sbctl dump-flows > sbflows2
++ovn-sbctl dump-flows > sbflows2
+ AT_CAPTURE_FILE([sbflows2])
+ 
+ as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
+@@ -6758,13 +6832,7 @@ test_dhcp() {
  }
  
  compare_dhcp_packets() {
@@ -6148,7 +9049,7 @@ index e127530f6..13f5575be 100644
  }
  
  AT_CAPTURE_FILE([sbflows])
-@@ -6962,8 +7009,9 @@ expected_dhcp_opts=0
+@@ -6962,8 +7030,9 @@ expected_dhcp_opts=0
  test_dhcp 11 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001
  
  # There is no reply for this. Check for the INFO log in ovn-controller.log
@@ -6160,7 +9061,7 @@ index e127530f6..13f5575be 100644
  
  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
  AT_CHECK([cat 2.packets], [0], [])
-@@ -7120,7 +7168,9 @@ ciaddr=`ip_to_hex 0 0 0 0`
+@@ -7120,7 +7189,9 @@ ciaddr=`ip_to_hex 0 0 0 0`
  request_ip=0
  expected_dhcp_opts=""
  test_dhcp 18 1 f00000000001 04 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
@@ -6171,7 +9072,7 @@ index e127530f6..13f5575be 100644
  
  # Send Etherboot.
  
-@@ -7138,7 +7188,7 @@ ovn-nbctl dhcp-options-set-options $d3 \
+@@ -7138,7 +7209,7 @@ ovn-nbctl dhcp-options-set-options $d3 \
     lease_time=3600 router=10.0.0.1 bootfile_name_alt=\"bootfile_name_alt\" \
     bootfile_name=\"bootfile\"
  
@@ -6180,7 +9081,7 @@ index e127530f6..13f5575be 100644
  
  offer_ip=`ip_to_hex 10 0 0 4`
  server_ip=`ip_to_hex 10 0 0 1`
-@@ -7156,9 +7206,6 @@ compare_dhcp_packets 1
+@@ -7156,9 +7227,6 @@ compare_dhcp_packets 1
  as northd
  OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE])
  
@@ -6190,7 +9091,7 @@ index e127530f6..13f5575be 100644
  northd_version=$(ovn-sbctl get SB_Global . options:northd_internal_version | sed s/\"//g)
  echo "northd version = $northd_version"
  
-@@ -7295,10 +7342,6 @@ check ovn-nbctl --wait=hv sync
+@@ -7295,10 +7363,6 @@ check ovn-nbctl --wait=hv sync
  # Start with 0 because the first request will not have NXT_RESUME
  n_resume=0
  
@@ -6201,7 +9102,7 @@ index e127530f6..13f5575be 100644
  # This shell function sends a DHCPv6 request packet
  # test_dhcpv6 INPORT SRC_MAC SRC_LLA DHCPv6_MSG_TYPE OFFER_IP OUTPORT...
  # The OUTPORTs (zero or more) list the VIFs on which the original DHCPv6
-@@ -7400,12 +7443,8 @@ test_dhcpv6_release() {
+@@ -7400,12 +7464,8 @@ test_dhcpv6_release() {
  check_packets() {
      local port=$1
  
@@ -6215,7 +9116,7 @@ index e127530f6..13f5575be 100644
      rm $port.expected
  }
  
-@@ -7508,7 +7547,7 @@ ovn-nbctl dhcp-options-set-options $d1 \
+@@ -7508,7 +7568,7 @@ ovn-nbctl dhcp-options-set-options $d1 \
      server_id=00:00:00:10:00:01 \
      bootfile_name_alt=\"bootfile_name_alt\" \
      bootfile_name=\"bootfile_name\"
@@ -6224,7 +9125,7 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif2 hv1/vif2
  
-@@ -7645,6 +7684,9 @@ ovn-nbctl lsp-add alice alice1 \
+@@ -7645,6 +7705,9 @@ ovn-nbctl lsp-add alice alice1 \
  wait_for_ports_up
  check ovn-nbctl --wait=hv sync
  
@@ -6234,7 +9135,7 @@ index e127530f6..13f5575be 100644
  # Send ip packets between foo1 and alice1
  src_mac="f00000010203"
  dst_mac="000001010203"
-@@ -8418,6 +8460,7 @@ check_dynamic_addresses() {
+@@ -8418,6 +8481,7 @@ check_dynamic_addresses() {
      check_row_count nb:Logical_Switch_Port 1 name="$1" dynamic_addresses="$arg"
  }
  
@@ -6242,7 +9143,7 @@ index e127530f6..13f5575be 100644
  # Add a port to a switch that does not have a subnet set, then set the
  # subnet which should result in an address being allocated for the port.
  ovn-nbctl --wait=hv set NB_Global . options:mac_prefix="0a:00:00:00:00:00"
-@@ -8733,7 +8776,7 @@ OVN_FOR_EACH_NORTHD([
+@@ -8733,7 +8797,7 @@ OVN_FOR_EACH_NORTHD([
  AT_SETUP([ipam connectivity])
  ovn_start
  
@@ -6251,7 +9152,7 @@ index e127530f6..13f5575be 100644
  
  # Test for a ping using dynamically allocated addresses.
  ovn-nbctl --wait=hv set NB_Global . options:mac_prefix="0a:00:00:00:00:00"
-@@ -9019,10 +9062,6 @@ packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111
+@@ -9019,10 +9083,6 @@ packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111
  # Send IP packet destined to 8.8.8.8 from lsp1lp2
  as hv1 ovs-appctl netdev-dummy/receive hv1-ls1lp2 $packet
  
@@ -6262,7 +9163,7 @@ index e127530f6..13f5575be 100644
  # ARP packet should be received with Target IP Address set to 192.168.1.254 and
  # not 8.8.8.8
  
-@@ -9078,9 +9117,6 @@ AT_CAPTURE_FILE([sbflows])
+@@ -9078,9 +9138,6 @@ AT_CAPTURE_FILE([sbflows])
  
  # Wait for packet to be received.
  OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 140])
@@ -6272,7 +9173,7 @@ index e127530f6..13f5575be 100644
  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros |sort | uniq > packets
  AT_CHECK([sort packets], [0], [dnl
  fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001
-@@ -9277,9 +9313,6 @@ ovn-nbctl list logical_router_port lrp0
+@@ -9277,9 +9334,6 @@ ovn-nbctl list logical_router_port lrp0
  ovn-nbctl show
  # Wait for packet to be received.
  OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 50])
@@ -6282,7 +9183,7 @@ index e127530f6..13f5575be 100644
  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | sort | uniq > packets
  expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001"
  echo $expected > expout
-@@ -9300,9 +9333,6 @@ ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" exclud
+@@ -9300,9 +9354,6 @@ ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router" exclud
  
  # Wait for packets to be received.
  OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 250])
@@ -6292,7 +9193,7 @@ index e127530f6..13f5575be 100644
  
  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets
  g0="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001"
-@@ -10721,10 +10751,6 @@ OVN_POPULATE_ARP
+@@ -10721,10 +10772,6 @@ OVN_POPULATE_ARP
  wait_for_ports_up
  check ovn-nbctl --wait=hv sync
  
@@ -6303,7 +9204,7 @@ index e127530f6..13f5575be 100644
  # Send ip packets between foo1 and bar1
  # (East-west traffic should flow normally)
  src_mac="f00000010203"
-@@ -11004,12 +11030,9 @@ test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
+@@ -11004,12 +11051,9 @@ test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
  # NXT_RESUMEs should be 1.
  OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
  
@@ -6318,7 +9219,7 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif1 hv1/vif1
  reset_pcap_file hv1-vif2 hv1/vif2
-@@ -11026,12 +11049,9 @@ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
+@@ -11026,12 +11070,9 @@ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
  # NXT_RESUMEs should be 2.
  OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
  
@@ -6333,7 +9234,7 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif1 hv1/vif1
  reset_pcap_file hv1-vif2 hv1/vif2
-@@ -11049,12 +11069,9 @@ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
+@@ -11049,12 +11090,9 @@ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
  # NXT_RESUMEs should be 3.
  OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
  
@@ -6348,7 +9249,7 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif1 hv1/vif1
  reset_pcap_file hv1-vif2 hv1/vif2
-@@ -11131,12 +11148,9 @@ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
+@@ -11131,12 +11169,9 @@ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
  # NXT_RESUMEs should be 5.
  OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
  
@@ -6363,7 +9264,7 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif1 hv1/vif1
  reset_pcap_file hv1-vif2 hv1/vif2
-@@ -11154,12 +11168,9 @@ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
+@@ -11154,12 +11189,9 @@ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
  # NXT_RESUMEs should be 6.
  OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
  
@@ -6378,7 +9279,7 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif1 hv1/vif1
  reset_pcap_file hv1-vif2 hv1/vif2
-@@ -11221,12 +11232,9 @@ test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
+@@ -11221,12 +11253,9 @@ test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
  # NXT_RESUMEs should be 9.
  OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
  
@@ -6393,7 +9294,7 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif1 hv1/vif1
  reset_pcap_file hv1-vif2 hv1/vif2
-@@ -11244,10 +11252,8 @@ test_dns6 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $
+@@ -11244,10 +11273,8 @@ test_dns6 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $
  # NXT_RESUMEs should be 10
  OVS_WAIT_UNTIL([test 10 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
  
@@ -6405,7 +9306,7 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif1 hv1/vif1
  reset_pcap_file hv1-vif2 hv1/vif2
-@@ -11273,12 +11279,9 @@ test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
+@@ -11273,12 +11300,9 @@ test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
  # NXT_RESUMEs should be 11.
  OVS_WAIT_UNTIL([test 11 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
  
@@ -6420,7 +9321,7 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif1 hv1/vif1
  reset_pcap_file hv1-vif2 hv1/vif2
-@@ -11296,12 +11299,9 @@ test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
+@@ -11296,12 +11320,9 @@ test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $d
  # NXT_RESUMEs should be 12.
  OVS_WAIT_UNTIL([test 12 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
  
@@ -6435,7 +9336,65 @@ index e127530f6..13f5575be 100644
  
  reset_pcap_file hv1-vif1 hv1/vif1
  reset_pcap_file hv1-vif2 hv1/vif2
-@@ -11481,30 +11481,7 @@ test_ip_packet()
+@@ -11313,6 +11334,57 @@ OVN_CLEANUP([hv1])
+ AT_CLEANUP
+ ])
+ 
++OVN_FOR_EACH_NORTHD([
++AT_SETUP([dns lookup : EDNS])
++OVN_CHECK_SCAPY_EDNS_CLIENT_SUBNET_SUPPORT()
++ovn_start
++
++check ovn-nbctl ls-add ls \
++    -- lsp-add ls lsp     \
++    -- lsp-set-addresses lsp "00:00:00:00:00:01 10.0.0.1"
++
++d=$(ovn-nbctl create dns records={})
++
++check ovn-nbctl set dns $d records:foo.ovn.org="10.0.0.42"
++check ovn-nbctl set Logical_switch ls dns_records="$d"
++
++net_add n1
++sim_add hv1
++
++as hv1
++ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.1
++check ovs-vsctl add-port br-int hv1-vif1 -- \
++    set interface hv1-vif1 external-ids:iface-id=lsp \
++    options:tx_pcap=hv1/vif1-tx.pcap \
++    options:rxq_pcap=hv1/vif1-rx.pcap
++
++OVN_POPULATE_ARP
++wait_for_ports_up
++check ovn-nbctl --wait=hv sync
++
++dns_req=$(fmt_pkt "Ether(dst='00:00:00:00:00:02', src='00:00:00:00:00:01') / \
++                   IP(dst='10.0.0.254', src='10.0.0.1') / \
++                   UDP(sport=42424, dport=53) / \
++                   DNS(rd=1, qd=DNSQR(qname='foo.ovn.org'), arcount=1, ar=[ \
++                       DNSRR(type='OPT', rclass=4096, \
++                             rdata=EDNS0ClientSubnet(source_plen=24, \
++                                                     address='10.0.0.1'))])")
++dns_reply=$(fmt_pkt "Ether(dst='00:00:00:00:00:01', src='00:00:00:00:00:02') / \
++                     IP(dst='10.0.0.1', src='10.0.0.254') / \
++                     UDP(sport=53, dport=42424,chksum=0) / \
++                     DNS(qr=1, qd=DNSQR(qname='foo.ovn.org'), \
++                         an=DNSRR(rrname='foo.ovn.org', type='A', ttl=3600, \
++                                  rdata='10.0.0.42'))")
++echo ${dns_reply} > expected
++
++as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 ${dns_req}
++OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv1/vif1-tx.pcap], [expected])
++
++OVN_CLEANUP([hv1])
++AT_CLEANUP
++])
++
+ OVN_FOR_EACH_NORTHD([
+ AT_SETUP([4 HV, 1 LS, 1 LR, packet test with HA distributed router gateway port])
+ ovn_start
+@@ -11481,30 +11553,7 @@ test_ip_packet()
      # Resend packet from foo1 to outside1
      check as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
  
@@ -6467,7 +9426,7 @@ index e127530f6..13f5575be 100644
  
      if test $backup_vswitchd_dead != 1; then
          # Check for backup gw only if vswitchd is alive
-@@ -12206,9 +12183,6 @@ OVN_WAIT_PATCH_PORT_FLOWS(["ln_port"], ["hv2"])
+@@ -12206,9 +12255,6 @@ OVN_WAIT_PATCH_PORT_FLOWS(["ln_port"], ["hv2"])
  
  # Wait for packets to be received.
  OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100])
@@ -6477,7 +9436,7 @@ index e127530f6..13f5575be 100644
  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets
  expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001"
  echo $expected > expout
-@@ -12240,21 +12214,12 @@ OVN_WAIT_PATCH_PORT_FLOWS(["ln_port"], ["hv3"])
+@@ -12240,21 +12286,12 @@ OVN_WAIT_PATCH_PORT_FLOWS(["ln_port"], ["hv3"])
  # Re-add nat-addresses option
  ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
  
@@ -6502,7 +9461,7 @@ index e127530f6..13f5575be 100644
  
  OVN_CLEANUP([hv1],[hv2],[hv3])
  
-@@ -12577,6 +12542,7 @@ AT_CLEANUP
+@@ -12577,6 +12614,7 @@ AT_CLEANUP
  
  OVN_FOR_EACH_NORTHD([
  AT_SETUP([IPv6 ND Router Solicitation responder])
@@ -6510,7 +9469,7 @@ index e127530f6..13f5575be 100644
  AT_KEYWORDS([ovn-nd_ra])
  ovn_start
  
-@@ -12643,76 +12609,120 @@ ovs-vsctl -- add-port br-int hv1-vif3 -- \
+@@ -12643,76 +12681,120 @@ ovs-vsctl -- add-port br-int hv1-vif3 -- \
  wait_for_ports_up
  check ovn-nbctl --wait=hv sync
  
@@ -6633,36 +9592,36 @@ index e127530f6..13f5575be 100644
  as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \
  --pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log
  
-+prefix="fdad:1234:5678::"
-+
- # MTU is not set and the address mode is set to slaac
+-# MTU is not set and the address mode is set to slaac
 -addr_mode=00
 -default_prefix_option_config=030440c0ffffffffffffffff00000000
 -src_mac=fa163e000002
 -src_lla=fe80000000000000f8163efffe000002
 -test_ipv6_ra 1 $src_mac $src_lla $addr_mode 0 $default_prefix_option_config
++prefix="fdad:1234:5678::"
+ 
+-# NXT_RESUME should be 1.
+-OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++# MTU is not set and the address mode is set to slaac
 +ra=$(prepare_ra_opt "" 0)
 +src_mac="fa:16:3e:00:00:02"
 +src_lla="fe80::f816:3eff:fe00:2"
  
--# NXT_RESUME should be 1.
--OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+-$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap  > 1.packets
 +test_ipv6_ra 1 $src_mac $src_lla "$ra" 0 $prefix "" "" ""
 +check_packets 1
  
--$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap  > 1.packets
-+# Check with RA with src being "::".
-+ovn-nbctl --wait=hv lsp-set-port-security lp1 ""
- 
 -cat 1.expected | cut -c -112 > expout
 -AT_CHECK([cat 1.packets | cut -c -112], [0], [expout])
-+test_ipv6_ra 1 $src_mac "::" "$ra" 0 $prefix "" "" ""
-+check_packets 1
++# Check with RA with src being "::".
++ovn-nbctl --wait=hv lsp-set-port-security lp1 ""
  
 -# Skipping the ICMPv6 checksum.
 -cat 1.expected | cut -c 117- > expout
 -AT_CHECK([cat 1.packets | cut -c 117-], [0], [expout])
--
++test_ipv6_ra 1 $src_mac "::" "$ra" 0 $prefix "" "" ""
++check_packets 1
+ 
 -rm -f *.expected
 -reset_pcap_file hv1-vif1 hv1/vif1
 -reset_pcap_file hv1-vif2 hv1/vif2
@@ -6671,7 +9630,7 @@ index e127530f6..13f5575be 100644
  
  # Set the MTU to 1500, send_periodic to false, preference to LOW
  ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:mtu=1500
-@@ -12726,33 +12736,14 @@ ovn-nbctl --wait=hv set Logical_Router_port lrp0 ipv6_ra_configs:route_info=HIGH
+@@ -12726,33 +12808,14 @@ ovn-nbctl --wait=hv set Logical_Router_port lrp0 ipv6_ra_configs:route_info=HIGH
  OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
  
  # addr_mode byte also includes router preference information
@@ -6693,16 +9652,16 @@ index e127530f6..13f5575be 100644
 -
 -cat 2.expected | cut -c -112 > expout
 -AT_CHECK([cat 2.packets | cut -c -112], [0], [expout])
--
--# Skipping the ICMPv6 checksum.
--cat 2.expected | cut -c 117- > expout
--AT_CHECK([cat 2.packets | cut -c 117-], [0], [expout])
 +ra=$(prepare_ra_opt "" 3)
 +routes=$(prepare_route_opt '1001::' 1 48)
 +routes="${routes}/$(prepare_route_opt '1002::' 3 96)"
 +src_mac="fa:16:3e:00:00:03"
 +src_lla="fe80::f816:3eff:fe00:3"
  
+-# Skipping the ICMPv6 checksum.
+-cat 2.expected | cut -c 117- > expout
+-AT_CHECK([cat 2.packets | cut -c 117-], [0], [expout])
+-
 -rm -f *.expected
 -reset_pcap_file hv1-vif1 hv1/vif1
 -reset_pcap_file hv1-vif2 hv1/vif2
@@ -6712,7 +9671,7 @@ index e127530f6..13f5575be 100644
  
  # Set the address mode to dhcpv6_stateful, router_preference to HIGH
  ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateful
-@@ -12763,30 +12754,12 @@ ovn-nbctl --wait=hv remove Logical_Router_Port lrp0 ipv6_ra_configs route_info
+@@ -12763,30 +12826,12 @@ ovn-nbctl --wait=hv remove Logical_Router_Port lrp0 ipv6_ra_configs route_info
  OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
  
  # addr_mode byte also includes router preference information
@@ -6726,7 +9685,10 @@ index e127530f6..13f5575be 100644
 -
 -# NXT_RESUME should be 3.
 -OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
--
++ra=$(prepare_ra_opt "stateful" 1)
++src_mac="fa:16:3e:00:00:04"
++src_lla="fe80::f816:3eff:fe00:4"
+ 
 -$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap  > 3.packets
 -
 -cat 3.expected | cut -c -112 > expout
@@ -6735,10 +9697,7 @@ index e127530f6..13f5575be 100644
 -# Skipping the ICMPv6 checksum.
 -cat 3.expected | cut -c 117- > expout
 -AT_CHECK([cat 3.packets | cut -c 117-], [0], [expout])
-+ra=$(prepare_ra_opt "stateful" 1)
-+src_mac="fa:16:3e:00:00:04"
-+src_lla="fe80::f816:3eff:fe00:4"
- 
+-
 -rm -f *.expected
 -reset_pcap_file hv1-vif1 hv1/vif1
 -reset_pcap_file hv1-vif2 hv1/vif2
@@ -6748,7 +9707,7 @@ index e127530f6..13f5575be 100644
  
  # Set the address mode to dhcpv6_stateless, reset router preference to default
  ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateless
-@@ -12795,46 +12768,27 @@ ovn-nbctl --wait=hv remove Logical_Router_Port lrp0 ipv6_ra_configs dnssl
+@@ -12795,46 +12840,27 @@ ovn-nbctl --wait=hv remove Logical_Router_Port lrp0 ipv6_ra_configs dnssl
  # Make sure that ovn-controller has installed the corresponding OF Flow.
  OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
  
@@ -6759,15 +9718,15 @@ index e127530f6..13f5575be 100644
 -mtu=000005dc
 -
 -test_ipv6_ra 1 $src_mac $src_lla $addr_mode $mtu $default_prefix_option_config
--
--# NXT_RESUME should be 4.
--OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
--
--$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap  > 1.packets
 +ra=$(prepare_ra_opt "stateless" 0)
 +src_mac="fa:16:3e:00:00:02"
 +src_lla="fe80::f816:3eff:fe00:2"
  
+-# NXT_RESUME should be 4.
+-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+-
+-$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap  > 1.packets
+-
 -cat 1.expected | cut -c -112 > expout
 -AT_CHECK([cat 1.packets | cut -c -112], [0], [expout])
 -
@@ -6807,7 +9766,34 @@ index e127530f6..13f5575be 100644
  
  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap  > 1.packets
  AT_CHECK([cat 1.packets], [0], [])
-@@ -13879,33 +13833,15 @@ as hv1 reset_pcap_file snoopvif hv1/snoopvif
+@@ -13612,7 +13638,7 @@ for chassis in gw1 hv1 hv2; do
+     echo "checking gw2 -> $chassis"
+     OVS_WAIT_UNTIL([
+     bfd_cfg=$(ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0)
+-    test "$bfd_cfg" = "enable=true min_rx=2000"
++    test "$bfd_cfg" = "check_tnl_key=true enable=true min_rx=2000"
+ ])
+ done
+ ovn-nbctl --wait=hv set NB_Global . options:"bfd-min-tx"=1500
+@@ -13620,7 +13646,7 @@ for chassis in gw1 hv1 hv2; do
+     echo "checking gw2 -> $chassis"
+     OVS_WAIT_UNTIL([
+     bfd_cfg=$(ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0)
+-    test "$bfd_cfg" = "enable=true min_rx=2000 min_tx=1500"
++    test "$bfd_cfg" = "check_tnl_key=true enable=true min_rx=2000 min_tx=1500"
+ ])
+ done
+ ovn-nbctl remove NB_Global . options "bfd-min-rx"
+@@ -13629,7 +13655,7 @@ for chassis in gw1 hv1 hv2; do
+     echo "checking gw2 -> $chassis"
+     OVS_WAIT_UNTIL([
+     bfd_cfg=$(ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0)
+-    test "$bfd_cfg" = "enable=true min_tx=1500 mult=15"
++    test "$bfd_cfg" = "check_tnl_key=true enable=true min_tx=1500 mult=15"
+ ])
+ done
+ 
+@@ -13879,33 +13905,15 @@ as hv1 reset_pcap_file snoopvif hv1/snoopvif
  # add nat-addresses option
  ovn-nbctl --wait=hv lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
  
@@ -6844,7 +9830,7 @@ index e127530f6..13f5575be 100644
  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv3_br_phys_tx
  echo "packets on hv3 br-phys tx"
  cat hv3_br_phys_tx
-@@ -13914,9 +13850,6 @@ AT_CHECK([grep $garp hv3_br_phys_tx | sort], [0], [])
+@@ -13914,9 +13922,6 @@ AT_CHECK([grep $garp hv3_br_phys_tx | sort], [0], [])
  
  # at this point, we invert the priority of the gw chassis between hv2 and hv3
  
@@ -6854,7 +9840,7 @@ index e127530f6..13f5575be 100644
  ovn-nbctl --wait=hv \
            --id=@gc0 create Gateway_Chassis \
                      name=outside_gw1 chassis_name=hv2 priority=1 -- \
-@@ -13924,24 +13857,17 @@ ovn-nbctl --wait=hv \
+@@ -13924,24 +13929,17 @@ ovn-nbctl --wait=hv \
                      name=outside_gw2 chassis_name=hv3 priority=10 -- \
            set Logical_Router_Port lrp0 'gateway_chassis=[@gc0,@gc1]'
  
@@ -6886,7 +9872,7 @@ index e127530f6..13f5575be 100644
  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv2_br_phys_tx
  AT_CHECK([grep $garp hv2_br_phys_tx | sort], [0], [])
  
-@@ -13974,25 +13900,11 @@ ovn-nbctl --wait=hv lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="rout
+@@ -13974,25 +13972,11 @@ ovn-nbctl --wait=hv lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="rout
  OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns nat_addresses find port_binding \
  logical_port=lrp0-rp | grep is_chassis | wc -l`])
  
@@ -6898,14 +9884,14 @@ index e127530f6..13f5575be 100644
 -
  garp="fffffffffffff00000000001810007de08060001080006040001f00000000001c0a80064000000000000c0a80064"
 -echo $garp > expout
-+echo $garp > expected_out
- 
+-
 -OVS_WAIT_UNTIL(
 -    [$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap > rcv_text
 -     exp_rcvd=$(cat rcv_text | grep $garp | wc -l)
 -     echo "expected received = $exp_rcvd"
 -     test $exp_rcvd -ge 1])
--
++echo $garp > expected_out
+ 
 -$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq >  hv1_snoopvif_tx
 -AT_CHECK([sort hv1_snoopvif_tx], [0], [expout])
 -$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv3_br_phys_tx
@@ -6915,7 +9901,7 @@ index e127530f6..13f5575be 100644
  $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv2_br_phys_tx
  AT_CHECK([grep $garp hv2_br_phys_tx | sort], [0], [])
  
-@@ -14333,10 +14245,6 @@ wc -l], [0], [4
+@@ -14333,10 +14317,6 @@ wc -l], [0], [4
  chassis_uuid=$(fetch_column Chassis _uuid name=hv1)
  wait_row_count Port_Binding 1 logical_port=cr-ip6_public chassis=$chassis_uuid
  
@@ -6926,7 +9912,7 @@ index e127530f6..13f5575be 100644
  # Test the IPv6 Neighbor Solicitation (NS) - nd_ns action for unknown MAC
  # addresses. ovn-controller should generate an IPv6 NS request for IPv6
  # packets whose MAC is unknown (in the ARP_REQUEST router pipeline stage.
-@@ -14888,6 +14796,10 @@ wait_column "$hv2_uuid" Port_Binding requested_additional_chassis logical_port=m
+@@ -14888,6 +14868,10 @@ wait_column "$hv2_uuid" Port_Binding requested_additional_chassis logical_port=m
  # ovn-installed on hv2 should guarantee that.
  OVS_WAIT_UNTIL([test `as hv2 ovs-vsctl get Interface migrator external_ids:ovn-installed` = '"true"'])
  
@@ -6937,7 +9923,7 @@ index e127530f6..13f5575be 100644
  # check that...
  # unicast from First arrives to hv1:Migrator
  # unicast from First arrives to hv2:Migrator
-@@ -14974,6 +14886,9 @@ wait_column "" Port_Binding requested_additional_chassis logical_port=migrator
+@@ -14974,6 +14958,9 @@ wait_column "" Port_Binding requested_additional_chassis logical_port=migrator
  # For instance, migrator port might still be up from prior to complete migration to hv2
  OVS_WAIT_UNTIL([test `as hv2 ovs-vsctl get Interface migrator external_ids:ovn-installed` = '"true"'])
  
@@ -6947,7 +9933,62 @@ index e127530f6..13f5575be 100644
  # check that...
  # unicast from Third doesn't arrive to hv1:Migrator
  # unicast from Third arrives to hv2:Migrator
-@@ -16188,7 +16103,7 @@ echo "verifying that lsp0 binding moves when requested-chassis is changed"
+@@ -16135,15 +16122,13 @@ sim_add hv1
+ as hv1
+ ovs-vsctl add-br br-phys
+ ovn_attach n1 br-phys 192.168.0.11
+-ovs-vsctl -- add-port br-int hv1-vif0 -- \
+-set Interface hv1-vif0 ofport-request=1
++ovs-vsctl -- add-port br-int hv1-vif0
+ 
+ sim_add hv2
+ as hv2
+ ovs-vsctl add-br br-phys
+ ovn_attach n1 br-phys 192.168.0.12
+-ovs-vsctl -- add-port br-int hv2-vif0 -- \
+-set Interface hv2-vif0 ofport-request=1
++ovs-vsctl -- add-port br-int hv2-vif0
+ 
+ # Allow only chassis hv1 to bind logical port lsp0.
+ ovn-nbctl lsp-set-options lsp0 requested-chassis=hv1
+@@ -16151,6 +16136,16 @@ ovn-nbctl lsp-set-options lsp0 requested-chassis=hv1
+ # Allow some time for ovn-northd and ovn-controller to catch up.
+ check ovn-nbctl --wait=hv sync
+ 
++OVS_WAIT_UNTIL([
++    hv1_ofport=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=hv1-vif0)
++    test 1 -le $hv1_ofport
++])
++
++OVS_WAIT_UNTIL([
++    hv2_ofport=$(as hv2 ovs-vsctl --bare --columns ofport find Interface name=hv2-vif0)
++    test 1 -le $hv2_ofport
++])
++
+ # Retrieve hv1 and hv2 chassis UUIDs from southbound database
+ wait_row_count Chassis 1 name=hv1
+ wait_row_count Chassis 1 name=hv2
+@@ -16166,7 +16161,7 @@ OVS_WAIT_UNTIL([test 1 = $(grep -c "Not claiming lport lsp0" hv2/ovn-controller.
+ wait_row_count Port_Binding 1 logical_port=lsp0 'chassis=[[]]'
+ 
+ # (2) Chassis hv2 should not add flows in OFTABLE_PHY_TO_LOG and OFTABLE_LOG_TO_PHY tables.
+-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [1], [])
++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=0 | grep in_port=$hv2_ofport], [1], [])
+ AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=65 | grep output], [1], [])
+ 
+ # (3) Chassis hv1 should bind lsp0 when physical to logical mapping exists on hv1.
+@@ -16179,8 +16174,8 @@ wait_column "$hv1_uuid" Port_Binding chassis logical_port=lsp0
+ 
+ # (4) Chassis hv1 should add flows in OFTABLE_PHY_TO_LOG and OFTABLE_LOG_TO_PHY tables.
+ as hv1 ovs-ofctl dump-flows br-int
+-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [0], [ignore])
+-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep actions=output:1], [0], [ignore])
++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=$hv1_ofport], [0], [ignore])
++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep actions=output:$hv1_ofport], [0], [ignore])
+ 
+ # (5) Chassis hv1 should release lsp0 binding and chassis hv2 should bind lsp0 when
+ # the requested chassis for lsp0 is changed from hv1 to hv2.
+@@ -16188,15 +16183,15 @@ echo "verifying that lsp0 binding moves when requested-chassis is changed"
  
  ovn-nbctl lsp-set-options lsp0 requested-chassis=hv2
  # We might see multiple "Releasing lport ...", when sb is read only
@@ -6956,7 +9997,18 @@ index e127530f6..13f5575be 100644
  
  wait_column "$hv2_uuid" Port_Binding chassis logical_port=lsp0
  
-@@ -16276,7 +16191,7 @@ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [0], [ig
+ # (6) Chassis hv2 should add flows and hv1 should not.
+-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [0], [ignore])
+-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=65 | grep actions=output:1], [0], [ignore])
++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=0 | grep in_port=$hv2_ofport], [0], [ignore])
++AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=65 | grep actions=output:$hv2_ofport], [0], [ignore])
+ 
+-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [1], [])
++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=$hv1_ofport], [1], [])
+ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep output], [1], [])
+ 
+ OVN_CLEANUP([hv1],[hv2])
+@@ -16276,7 +16271,7 @@ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [0], [ig
  AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep actions=output:1], [0], [ignore])
  
  check ovn-nbctl --wait=hv lsp-set-options lsp0 requested-chassis=non-existant-chassis
@@ -6965,97 +10017,137 @@ index e127530f6..13f5575be 100644
  check ovn-nbctl --wait=hv sync
  wait_column '' Port_Binding chasssi logical_port=lsp0
  AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [1], [])
-@@ -18989,6 +18904,46 @@ for sf in 0 1; do
+@@ -18989,55 +18984,8 @@ for sf in 0 1; do
      done
  done
  
-+check_packets() {
+-# Test allow rule
+-#----------------
+-ovn-nbctl acl-del lsw0
+-# drop all packets to 11 and 21.
+-ovn-nbctl acl-add lsw0 to-lport 5000 'outport == @pg1 && eth.src == $set1' drop
+-# allow 0x1234 between 11 and 21
+-check ovn-nbctl --wait=hv --log --severity=info --name=allow-acl acl-add lsw0 to-lport 6000 'outport == @pg1 && eth.src == $set1 && eth.type == 0x1234' allow
+-for sf in 0 1; do
+-    if test ${sf} = 1; then
+-        # Add a stateful rule and re-run the check to make sure the
+-        # allow rule is still effective..
+-        check ovn-nbctl --wait=hv acl-add lsw0 from-lport 2000  'inport == "lp31" && ip' allow-related
+-    fi
+-    # dump information and flows with counters
+-    ovn-sbctl dump-flows -- list multicast_group > sbflows$sf
+-    AT_CAPTURE_FILE([sbflows0])
+-    AT_CAPTURE_FILE([sbflows1])
+-    for is in 1 2 3; do
+-        s=${is}1
+-        for id in 1 2 3; do
+-            d=${id}1
+-
+-            if test $d != $s;
+-            then
+-                test_packet $s f000000000$d f000000000$s 1234 $d # allow 1234 to 11, 21, and 31
+-            fi
+-        done
+-
+-        # Broadcast and multicast. Allow from one to the other 2.
+-        if test ${is} = 1; then
+-            bcast="21 31"
+-        elif test ${is} = 2; then
+-            bcast="11 31"
+-        else
+-            bcast="11 21"
+-        fi
+-        test_packet $s ffffffffffff f000000000$s 1234 $bcast
+-        test_packet $s 010000000000 f000000000$s 1234 $bcast
+-    done
+-done
+-
+-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int > offlows1
+-as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int > offlows2
+-as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int > offlows3
+-
+-# Now check the packets actually received against the ones expected.
+-AT_CAPTURE_FILE([expected])
+-AT_CAPTURE_FILE([received])
+ check_packets() {
 +    n_allowed=$1
-+    > expected
-+    > received
-+    for i in 1 2 3; do
-+        echo "--- hv$i vif${i}1" | tee -a expected >> received
-+        sort ${i}1.expected >> expected
-+        $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv$i/vif${i}1-tx.pcap | sort >> received
-+        echo | tee -a expected >> received
-+    done
-+
-+    # need to verify the log for ACL hit as well, since in the allow case
-+    # (unlike the drop case) it is tricky to pass just with the expected;
-+    # since with the stateful rule the packet will still get by (default
-+    # rule) even if it doesn't hit the allow rule.
-+    # The hit count for the ACL is 6 (1 unicast + 2 non-unicast) * 2
-+    # (with/without stateful rule) for hv1 and hv2, each.
-+    cat >>expected <<EOF
-+--- acl logging
-+hv1_drop hit 6
-+hv2_drop hit 6
+     > expected
+     > received
+     for i in 1 2 3; do
+@@ -19057,8 +19005,8 @@ check_packets() {
+ --- acl logging
+ hv1_drop hit 6
+ hv2_drop hit 6
+-hv1_allow hit 6
+-hv2_allow hit 6
 +hv1_allow hit $n_allowed
 +hv2_allow hit $n_allowed
-+EOF
-+
-+cat >>received <<EOF
-+--- acl logging
-+hv1_drop hit `grep -c 'acl_log.*|INFO|name="drop-acl"' hv1/ovn-controller.log`
-+hv2_drop hit `grep -c 'acl_log.*|INFO|name="drop-acl"' hv2/ovn-controller.log`
-+hv1_allow hit `grep -c 'acl_log.*|INFO|name="allow-acl"' hv1/ovn-controller.log`
-+hv2_allow hit `grep -c 'acl_log.*|INFO|name="allow-acl"' hv2/ovn-controller.log`
-+EOF
-+
-+    $at_diff expected received >/dev/null
-+}
+ EOF
+ 
+ cat >>received <<EOF
+@@ -19071,7 +19019,60 @@ EOF
+ 
+     $at_diff expected received >/dev/null
+ }
+-OVS_WAIT_UNTIL([check_packets], [$at_diff -F'^---' expected received])
 +
 +# We need to wait and check here that packets are received as they should as otherwise packets
 +# which were just sent might by handled after setting next ACL (allow) rules.
 +OVS_WAIT_UNTIL([check_packets 0], [$at_diff -F'^---' expected received])
 +
- # Test allow rule
- #----------------
- ovn-nbctl acl-del lsw0
-@@ -19037,41 +18992,7 @@ as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int > offlows3
- # Now check the packets actually received against the ones expected.
- AT_CAPTURE_FILE([expected])
- AT_CAPTURE_FILE([received])
--check_packets() {
--    > expected
--    > received
--    for i in 1 2 3; do
--        echo "--- hv$i vif${i}1" | tee -a expected >> received
--        sort ${i}1.expected >> expected
--        $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv$i/vif${i}1-tx.pcap | sort >> received
--        echo | tee -a expected >> received
--    done
--
--    # need to verify the log for ACL hit as well, since in the allow case
--    # (unlike the drop case) it is tricky to pass just with the expected;
--    # since with the stateful rule the packet will still get by (default
--    # rule) even if it doesn't hit the allow rule.
--    # The hit count for the ACL is 6 (1 unicast + 2 non-unicast) * 2
--    # (with/without stateful rule) for hv1 and hv2, each.
--    cat >>expected <<EOF
----- acl logging
--hv1_drop hit 6
--hv2_drop hit 6
--hv1_allow hit 6
--hv2_allow hit 6
--EOF
--
--cat >>received <<EOF
----- acl logging
--hv1_drop hit `grep -c 'acl_log.*|INFO|name="drop-acl"' hv1/ovn-controller.log`
--hv2_drop hit `grep -c 'acl_log.*|INFO|name="drop-acl"' hv2/ovn-controller.log`
--hv1_allow hit `grep -c 'acl_log.*|INFO|name="allow-acl"' hv1/ovn-controller.log`
--hv2_allow hit `grep -c 'acl_log.*|INFO|name="allow-acl"' hv2/ovn-controller.log`
--EOF
--
--    $at_diff expected received >/dev/null
--}
--OVS_WAIT_UNTIL([check_packets], [$at_diff -F'^---' expected received])
++# Test allow rule
++#----------------
++ovn-nbctl acl-del lsw0
++# drop all packets to 11 and 21.
++ovn-nbctl acl-add lsw0 to-lport 5000 'outport == @pg1 && eth.src == $set1' drop
++# allow 0x1234 between 11 and 21
++check ovn-nbctl --wait=hv --log --severity=info --name=allow-acl acl-add lsw0 to-lport 6000 'outport == @pg1 && eth.src == $set1 && eth.type == 0x1234' allow
++for sf in 0 1; do
++    if test ${sf} = 1; then
++        # Add a stateful rule and re-run the check to make sure the
++        # allow rule is still effective..
++        check ovn-nbctl --wait=hv acl-add lsw0 from-lport 2000  'inport == "lp31" && ip' allow-related
++    fi
++    # dump information and flows with counters
++    ovn-sbctl dump-flows -- list multicast_group > sbflows$sf
++    AT_CAPTURE_FILE([sbflows0])
++    AT_CAPTURE_FILE([sbflows1])
++    for is in 1 2 3; do
++        s=${is}1
++        for id in 1 2 3; do
++            d=${id}1
++
++            if test $d != $s;
++            then
++                test_packet $s f000000000$d f000000000$s 1234 $d # allow 1234 to 11, 21, and 31
++            fi
++        done
++
++        # Broadcast and multicast. Allow from one to the other 2.
++        if test ${is} = 1; then
++            bcast="21 31"
++        elif test ${is} = 2; then
++            bcast="11 31"
++        else
++            bcast="11 21"
++        fi
++        test_packet $s ffffffffffff f000000000$s 1234 $bcast
++        test_packet $s 010000000000 f000000000$s 1234 $bcast
++    done
++done
++
++as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int > offlows1
++as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int > offlows2
++as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int > offlows3
++
++# Now check the packets actually received against the ones expected.
++AT_CAPTURE_FILE([expected])
++AT_CAPTURE_FILE([received])
 +OVS_WAIT_UNTIL([check_packets 6], [$at_diff -F'^---' expected received])
  
  OVN_CLEANUP([hv1],[hv2],[hv3])
  
-@@ -19790,11 +19711,6 @@ test_dhcp() {
+@@ -19790,11 +19791,6 @@ test_dhcp() {
      as hv1 ovs-appctl netdev-dummy/receive hv${inport}-ext${inport} $request
  }
  
@@ -7067,7 +10159,7 @@ index e127530f6..13f5575be 100644
  # This shell function sends a DHCPv6 request packet
  # test_dhcpv6 INPORT SRC_MAC SRC_LLA DHCPv6_MSG_TYPE OFFER_IP OUTPORT...
  # The OUTPORTs (zero or more) list the VIFs on which the original DHCPv6
-@@ -19876,12 +19792,9 @@ OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
+@@ -19876,12 +19872,9 @@ OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
  # NXT_RESUMEs should be 0 in hv2.
  OVS_WAIT_UNTIL([test 0 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
  
@@ -7082,7 +10174,7 @@ index e127530f6..13f5575be 100644
  
  # ovs-ofctl also resumes the packets and this causes other ports to receive
  # the DHCP request packet. So reset the pcap files so that its easier to test.
-@@ -19903,13 +19816,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
+@@ -19903,13 +19896,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
  # NXT_RESUMEs should be 0 in hv2.
  OVS_WAIT_UNTIL([test 0 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
  
@@ -7098,7 +10190,7 @@ index e127530f6..13f5575be 100644
  
  rm -f ext1_v6.expected
  rm -f ext1_v6.packets
-@@ -19969,12 +19878,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
+@@ -19969,12 +19958,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
  # NXT_RESUMEs should be 1 in hv2.
  OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
  
@@ -7113,7 +10205,7 @@ index e127530f6..13f5575be 100644
  
  # ovs-ofctl also resumes the packets and this causes other ports to receive
  # the DHCP request packet. So reset the pcap files so that its easier to test.
-@@ -19995,13 +19901,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
+@@ -19995,13 +19981,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
  # NXT_RESUMEs should be 2 in hv2.
  OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
  
@@ -7129,7 +10221,7 @@ index e127530f6..13f5575be 100644
  
  rm -f ext1_v6.expected
  rm -f ext1_v6.packets
-@@ -20077,12 +19979,9 @@ OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
+@@ -20077,12 +20059,9 @@ OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
  # NXT_RESUMEs should be 2 in hv2.
  OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
  
@@ -7144,7 +10236,7 @@ index e127530f6..13f5575be 100644
  
  # ovs-ofctl also resumes the packets and this causes other ports to receive
  # the DHCP request packet. So reset the pcap files so that its easier to test.
-@@ -20104,13 +20003,9 @@ OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
+@@ -20104,13 +20083,9 @@ OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
  # NXT_RESUMEs should be 2 in hv2.
  OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
  
@@ -7160,7 +10252,7 @@ index e127530f6..13f5575be 100644
  
  rm -f ext1_v6.expected
  rm -f ext1_v6.packets
-@@ -20158,12 +20053,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
+@@ -20158,12 +20133,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
  # NXT_RESUMEs should be 1 in hv3.
  OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor0_hv3.log | grep -c NXT_RESUME`])
  
@@ -7175,7 +10267,7 @@ index e127530f6..13f5575be 100644
  
  # ovs-ofctl also resumes the packets and this causes other ports to receive
  # the DHCP request packet. So reset the pcap files so that its easier to test.
-@@ -20188,13 +20080,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
+@@ -20188,13 +20160,9 @@ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
  # NXT_RESUMEs should be 2 in hv3.
  OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv3.log | grep -c NXT_RESUME`])
  
@@ -7191,31 +10283,7 @@ index e127530f6..13f5575be 100644
  
  # disconnect hv3 from the network, hv1 should take over
  as hv3
-@@ -20568,12 +20456,12 @@ test_ip_packet_larger() {
-         expected=${expected}0000000000000000000000000000
-         echo $expected > br_phys_n1.expected
-     else
--        src_ip=`ip_to_hex 10 0 0 1`
-+        src_ip=`ip_to_hex 172.168.0.100`
-         dst_ip=`ip_to_hex 10 0 0 3`
-         # pkt len should be 146 (28 (icmp packet) + 118 (orig ip + payload))
-         reply_pkt_len=008e
-         ip_csum=fc97
--        icmp_reply=${src_mac}${dst_mac}08004500${reply_pkt_len}00004000fe01686b
-+        icmp_reply=${src_mac}${dst_mac}08004500${reply_pkt_len}00004000fe01c55f
-         icmp_reply=${icmp_reply}${src_ip}${dst_ip}0304${ip_csum}0000$(printf "%04x" $mtu)
-         icmp_reply=${icmp_reply}4500${pkt_len}000000003f01c4dd
-         icmp_reply=${icmp_reply}${orig_packet_l3}
-@@ -20665,7 +20553,7 @@ test_ip6_packet_larger() {
- 
-     local ipv6_src=10000000000000000000000000000003
-     local ipv6_dst=20000000000000000000000000000002
--    local ipv6_rt=10000000000000000000000000000001
-+    local ipv6_rt=20000000000000000000000000000001
- 
-     local payload=0000000000000000000000000000000000000000
-     local payload=${payload}0000000000000000000000000000000000000000
-@@ -21223,6 +21111,7 @@ OVN_FOR_EACH_NORTHD([
+@@ -21223,6 +21191,7 @@ OVN_FOR_EACH_NORTHD([
  AT_SETUP([ipam to non-ipam])
  ovn_start
  
@@ -7223,7 +10291,7 @@ index e127530f6..13f5575be 100644
  ovn-nbctl --wait=hv set NB_Global . options:mac_prefix="0a:00:00:00:00:00"
  ovn-nbctl ls-add sw0
  ovn-nbctl lsp-add sw0 p0 -- lsp-set-addresses p0 dynamic
-@@ -23426,7 +23315,7 @@ check ovn-nbctl set Logical_Switch sw2 \
+@@ -23426,7 +23395,7 @@ check ovn-nbctl set Logical_Switch sw2 \
      other_config:mcast_querier="false"
  
  # Enable multicast snooping on sw3.
@@ -7232,7 +10300,7 @@ index e127530f6..13f5575be 100644
      other_config:mcast_querier="false" \
      other_config:mcast_snoop="true"
  
-@@ -23465,7 +23354,7 @@ OVS_WAIT_UNTIL(
+@@ -23465,7 +23434,7 @@ OVS_WAIT_UNTIL(
    [$at_diff -F'^---' exp rcv])
  
  # Enable multicast relay on rtr
@@ -7241,7 +10309,7 @@ index e127530f6..13f5575be 100644
  
  AT_CAPTURE_FILE([sbflows6])
  ovn-sbctl dump-flows > sbflows6
-@@ -26413,10 +26302,13 @@ check ovn-nbctl lsp-add ts ts-lr3 \
+@@ -26413,10 +26382,13 @@ check ovn-nbctl lsp-add ts ts-lr3 \
  wait_for_ports_up
  
  ovn_as az1
@@ -7255,7 +10323,7 @@ index e127530f6..13f5575be 100644
  check ovn-nbctl lsp-set-options ts-lr1 requested-chassis=hv1
  
  dnl Enable unregistered IP multicast flooding and IP multicast relay.
-@@ -26625,10 +26517,13 @@ check ovn-nbctl lsp-add ts ts-lr3 \
+@@ -26625,10 +26597,13 @@ check ovn-nbctl lsp-add ts ts-lr3 \
  wait_for_ports_up
  
  ovn_as az1
@@ -7269,7 +10337,7 @@ index e127530f6..13f5575be 100644
  check ovn-nbctl lsp-set-options ts-lr1 requested-chassis=hv1
  
  dnl Enable IP multicast snooping and IP multicast relay.  Reports are
-@@ -27984,8 +27879,9 @@ ovn-nbctl ls-add ls1
+@@ -27984,8 +27959,9 @@ ovn-nbctl ls-add ls1
  ovn-nbctl --wait=sb lsp-add ls1 lsp1
  
  # Simulate the fact that lsp1 had been previously bound on hv1.
@@ -7281,7 +10349,7 @@ index e127530f6..13f5575be 100644
      -- set Port_Binding lsp1 chassis=@c
  
  as hv1
-@@ -28011,8 +27907,9 @@ ovn-nbctl ls-add ls1
+@@ -28011,8 +27987,9 @@ ovn-nbctl ls-add ls1
  ovn-nbctl --wait=sb lsp-add ls1 lsp1
  
  # Simulate the fact that lsp1 had been previously bound on hv1.
@@ -7293,7 +10361,7 @@ index e127530f6..13f5575be 100644
      -- set Port_Binding lsp1 chassis=@c
  
  as hv1
-@@ -28137,9 +28034,10 @@ OVS_WAIT_UNTIL([
+@@ -28137,9 +28114,10 @@ OVS_WAIT_UNTIL([
  ])
  
  as hv1 check ovs-ofctl del-flows br-phys
@@ -7305,7 +10373,25 @@ index e127530f6..13f5575be 100644
  table=0, priority=100, pkt_mark=0x64 actions=drop
  table=0, priority=100, pkt_mark=0x2 actions=drop
  table=0, priority=100, pkt_mark=0x3 actions=drop
-@@ -30785,7 +30683,6 @@ check ovn-nbctl --wait=hv ls-lb-del sw0 lb-ipv6
+@@ -28966,7 +28944,7 @@ AT_CHECK([
+         grep "priority=200" | \
+         grep -c "move:NXM_NX_CT_LABEL\\[[\\]]->NXM_NX_XXREG1\\[[\\]],move:NXM_NX_XXREG1\\[[32..79\\]]->NXM_OF_ETH_DST"
+     done; :], [0], [dnl
+-6
++2
+ 1
+ 0
+ 0
+@@ -29091,7 +29069,7 @@ AT_CHECK([
+         grep "priority=200" | \
+         grep -c "move:NXM_NX_CT_LABEL\\[[\\]]->NXM_NX_XXREG1\\[[\\]],move:NXM_NX_XXREG1\\[[32..79\\]]->NXM_OF_ETH_DST"
+     done; :], [0], [dnl
+-6
++2
+ 1
+ 0
+ 0
+@@ -30785,7 +30763,6 @@ check ovn-nbctl --wait=hv ls-lb-del sw0 lb-ipv6
  # original destination tuple.
  #
  # ovn-controller should fall back to matching on ct_nw_dst()/ct_tp_dst().
@@ -7313,7 +10399,7 @@ index e127530f6..13f5575be 100644
  as northd ovn-appctl -t NORTHD_TYPE pause
  
  check ovn-sbctl \
-@@ -30836,7 +30733,6 @@ OVS_WAIT_FOR_OUTPUT([as hv2 ovs-ofctl dump-flows br-int table=70 | ofctl_strip_a
+@@ -30836,7 +30813,6 @@ OVS_WAIT_FOR_OUTPUT([as hv2 ovs-ofctl dump-flows br-int table=70 | ofctl_strip_a
  
  # Resume ovn-northd.
  as northd ovn-appctl -t NORTHD_TYPE resume
@@ -7321,7 +10407,7 @@ index e127530f6..13f5575be 100644
  check ovn-nbctl --wait=hv sync
  
  as hv2 ovs-vsctl del-port hv2-vif1
-@@ -30957,9 +30853,6 @@ AT_CHECK([grep -c $northd_version hv1/ovn-controller.log], [0], [1
+@@ -30957,9 +30933,6 @@ AT_CHECK([grep -c $northd_version hv1/ovn-controller.log], [0], [1
  as northd
  OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE])
  
@@ -7331,7 +10417,7 @@ index e127530f6..13f5575be 100644
  check ovn-sbctl set SB_Global . options:northd_internal_version=foo
  
  as hv1
-@@ -31101,7 +30994,6 @@ AS_BOX([ovn-controller should not reset Port_Binding.up without northd])
+@@ -31101,7 +31074,6 @@ AS_BOX([ovn-controller should not reset Port_Binding.up without northd])
  # Pause northd and clear the "up" field to simulate older ovn-northd
  # versions writing to the Southbound DB.
  as northd ovn-appctl -t NORTHD_TYPE pause
@@ -7339,7 +10425,7 @@ index e127530f6..13f5575be 100644
  
  as hv1 ovn-appctl -t ovn-controller debug/pause
  check ovn-sbctl clear Port_Binding lsp1 up
-@@ -31117,7 +31009,6 @@ check_column "" Port_Binding up logical_port=lsp1
+@@ -31117,7 +31089,6 @@ check_column "" Port_Binding up logical_port=lsp1
  # Once northd should explicitly set the Port_Binding.up field to 'false' and
  # ovn-controller sets it to 'true' as soon as the update is processed.
  as northd ovn-appctl -t NORTHD_TYPE resume
@@ -7347,7 +10433,7 @@ index e127530f6..13f5575be 100644
  wait_column "true" Port_Binding up logical_port=lsp1
  wait_column "true" nb:Logical_Switch_Port up name=lsp1
  
-@@ -31272,7 +31163,6 @@ check ovn-nbctl lsp-set-addresses sw0-p4 "00:00:00:00:00:04 192.168.47.4"
+@@ -31272,7 +31243,6 @@ check ovn-nbctl lsp-set-addresses sw0-p4 "00:00:00:00:00:04 192.168.47.4"
  # will be sent in one transaction.
  
  check as northd ovn-appctl -t NORTHD_TYPE pause
@@ -7355,7 +10441,7 @@ index e127530f6..13f5575be 100644
  
  check ovn-nbctl lsp-add sw0 sw0-p1
  check ovn-nbctl lsp-set-addresses sw0-p1 "00:00:00:00:00:01 192.168.47.1"
-@@ -31453,10 +31343,6 @@ send_icmp_packet() {
+@@ -31453,10 +31423,6 @@ send_icmp_packet() {
      as hv$hv ovs-appctl netdev-dummy/receive hv$hv-vif$inport $packet
  }
  
@@ -7366,7 +10452,7 @@ index e127530f6..13f5575be 100644
  AS_BOX([Wait for all ports to be up])
  wait_for_ports_up
  
-@@ -33727,7 +33613,6 @@ check as hv1 ovn-appctl -t ovn-controller exit
+@@ -33727,7 +33693,6 @@ check as hv1 ovn-appctl -t ovn-controller exit
  
  # Pause northd to guarantee that ovn-controller starts before requested_chassis
  # column is filled.
@@ -7374,7 +10460,7 @@ index e127530f6..13f5575be 100644
  check as northd ovn-appctl -t ovn-northd pause
  
  # Wait until requested_chassis is empty
-@@ -33741,7 +33626,6 @@ wait_column "hv1" Chassis name
+@@ -33741,7 +33706,6 @@ wait_column "hv1" Chassis name
  
  # Start northd and wait for events to be processed
  check as northd ovn-appctl -t ovn-northd resume
@@ -7382,7 +10468,7 @@ index e127530f6..13f5575be 100644
  
  wait_for_ports_up lsp1
  
-@@ -34471,7 +34355,7 @@ dnat_zone=$(ovs-ofctl dump-flows br-int table=$DNAT_TABLE,metadata=0x${lr0_dp_ke
+@@ -34471,7 +34435,7 @@ dnat_zone=$(ovs-ofctl dump-flows br-int table=$DNAT_TABLE,metadata=0x${lr0_dp_ke
  if test -n "$dnat_zone"; then
    dnat_zone=${dnat_zone::-1}
  fi
@@ -7391,7 +10477,7 @@ index e127530f6..13f5575be 100644
  if test -n "$snat_zone"; then
    snat_zone=${snat_zone::-1}
  fi
-@@ -34488,7 +34372,7 @@ dnat_zone=$(ovs-ofctl dump-flows br-int table=$DNAT_TABLE,metadata=0x${lr0_dp_ke
+@@ -34488,7 +34452,7 @@ dnat_zone=$(ovs-ofctl dump-flows br-int table=$DNAT_TABLE,metadata=0x${lr0_dp_ke
  if test -n "$dnat_zone"; then
    dnat_zone=${dnat_zone::-1}
  fi
@@ -7400,7 +10486,7 @@ index e127530f6..13f5575be 100644
  if test -n "$snat_zone"; then
    snat_zone=${snat_zone::-1}
  fi
-@@ -34572,6 +34456,7 @@ AT_CLEANUP
+@@ -34572,6 +34536,7 @@ AT_CLEANUP
  
  OVN_FOR_EACH_NORTHD([
  AT_SETUP([MAC binding aging])
@@ -7408,7 +10494,29 @@ index e127530f6..13f5575be 100644
  ovn_start
  
  net_add n1
-@@ -35389,37 +35274,6 @@ OVN_CLEANUP([hv1],[hv2])
+@@ -34748,8 +34713,8 @@ check ovn-nbctl lsp-add sw0 sw0-p1
+ check ovn-nbctl lsp-add sw0 lsp
+ check ovn-nbctl lsp-set-type lsp router
+ check ovn-nbctl lsp-set-options lsp router-port=lrp
+-check ovn-nbctl lsp-set-addresses lsp  00:00:00:00:00:1
+-check ovn-nbctl lrp-add ro0 lrp 00:00:00:00:00:1 aef0:0:0:0:0:0:0:1/64
++check ovn-nbctl lsp-set-addresses lsp  00:00:00:00:00:01
++check ovn-nbctl lrp-add ro0 lrp 00:00:00:00:00:01 aef0:0:0:0:0:0:0:1/64
+ check ovn-nbctl set Logical_Router_Port lrp ipv6_ra_configs:send_periodic=true \
+         -- set Logical_Router_Port lrp ipv6_ra_configs:address_mode=slaac \
+         -- set Logical_Router_Port lrp ipv6_ra_configs:mtu=1280 \
+@@ -34843,8 +34808,8 @@ check ovn-nbctl lsp-add sw0 sw0-p1
+ check ovn-nbctl lsp-add sw0 lsp
+ check ovn-nbctl lsp-set-type lsp router
+ check ovn-nbctl lsp-set-options lsp router-port=lrp
+-check ovn-nbctl lsp-set-addresses lsp  00:00:00:00:00:1
+-check ovn-nbctl --wait=hv lrp-add ro0 lrp 00:00:00:00:00:1 aef0:0:0:0:0:0:0:1/64
++check ovn-nbctl lsp-set-addresses lsp  00:00:00:00:00:01
++check ovn-nbctl --wait=hv lrp-add ro0 lrp 00:00:00:00:00:01 aef0:0:0:0:0:0:0:1/64
+ check ovn-nbctl --wait=hv lsp-del lsp
+ check ovn-nbctl lrp-del lrp
+ check ovn-nbctl --wait=hv sync
+@@ -35389,37 +35354,6 @@ OVN_CLEANUP([hv1],[hv2])
  AT_CLEANUP
  ])
  
@@ -7446,7 +10554,7 @@ index e127530f6..13f5575be 100644
  OVN_FOR_EACH_NORTHD([
  AT_SETUP([Logical flows with Chassis_Template_Var references])
  AT_KEYWORDS([templates])
-@@ -35795,7 +35649,7 @@ as hv1 ovs-vsctl set-ssl \
+@@ -35795,7 +35729,7 @@ as hv1 ovs-vsctl set-ssl \
  echo hv3 > ${OVN_SYSCONFDIR}/test_hv
  
  # the last argument is passed to ovn-controller through cli
@@ -7455,7 +10563,7 @@ index e127530f6..13f5575be 100644
  
  sim_add hv2
  as hv2
-@@ -36728,7 +36582,6 @@ check ovn-nbctl lsp-add ls2 public2
+@@ -36728,7 +36662,6 @@ check ovn-nbctl lsp-add ls2 public2
  check ovn-nbctl lsp-set-addresses public2 unknown
  check ovn-nbctl lsp-set-type public2 localnet
  check ovn-nbctl --wait=sb set Logical_Switch_Port public2 options:qos_min_rate=6000000000 options:qos_max_rate=7000000000 options:qos_burst=8000000000 options:network_name=phys
@@ -7463,7 +10571,7 @@ index e127530f6..13f5575be 100644
  
  # Let's now send ovn controller to sleep, so it will receive both ofport notification and ls deletion simultaneously
  sleep_controller hv-1
-@@ -36861,14 +36714,14 @@ check ovn-nbctl ls-add sw0
+@@ -36861,14 +36794,14 @@ check ovn-nbctl ls-add sw0
  check ovn-nbctl lsp-add sw0 sw0-port1
  check ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 192.168.0.2"
  
@@ -7481,7 +10589,7 @@ index e127530f6..13f5575be 100644
  check ovn-appctl -t ovn-controller vlog/set dbg:main
  
  wait_for_ports_up
-@@ -37005,3 +36858,548 @@ AT_CHECK([grep -c "NXT_CT_FLUSH_ZONE" hv1/ovs-vswitchd.log], [0], [dnl
+@@ -37005,3 +36938,730 @@ AT_CHECK([grep -c "NXT_CT_FLUSH_ZONE" hv1/ovs-vswitchd.log], [0], [dnl
  OVN_CLEANUP([hv1])
  AT_CLEANUP
  ])
@@ -8030,6 +11138,188 @@ index e127530f6..13f5575be 100644
 +
 +AT_CLEANUP
 +])
++
++AT_SETUP([read-only sb db:pssl access with ssl-ciphers and ssl-protocols])
++AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
++PKIDIR="$(cd $abs_top_builddir/tests && pwd)"
++AT_SKIP_IF([expr "$PKIDIR" : ".*[[ 	'\"
++\\]]"])
++
++: > .$1.db.~lock~
++ovsdb-tool create ovn-sb.db "$abs_top_srcdir"/ovn-sb.ovsschema
++
++# Add read-only remote to sb ovsdb-server
++AT_CHECK(
++  [ovsdb-tool transact ovn-sb.db \
++     ['["OVN_Southbound",
++       {"op": "insert",
++        "table": "SB_Global",
++        "row": {
++          "connections": ["set", [["named-uuid", "xyz"]]]}},
++       {"op": "insert",
++        "table": "Connection",
++        "uuid-name": "xyz",
++        "row": {"target": "pssl:0:127.0.0.1",
++               "read_only": true}}]']], [0], [ignore], [ignore])
++
++start_daemon ovsdb-server --remote=punix:ovn-sb.sock \
++                          --remote=db:OVN_Southbound,SB_Global,connections \
++                          --private-key="$PKIDIR/testpki-test2-privkey.pem" \
++                          --certificate="$PKIDIR/testpki-test2-cert.pem" \
++                          --ca-cert="$PKIDIR/testpki-cacert.pem" \
++                          --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                          --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++                          ovn-sb.db
++
++PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
++
++# read-only accesses should succeed
++AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
++                    --private-key=$PKIDIR/testpki-test-privkey.pem \
++                    --certificate=$PKIDIR/testpki-test-cert.pem \
++                    --ca-cert=$PKIDIR/testpki-cacert.pem \
++                    --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                    --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++                    list SB_Global], [0], [stdout], [ignore])
++AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
++                    --private-key=$PKIDIR/testpki-test-privkey.pem \
++                    --certificate=$PKIDIR/testpki-test-cert.pem \
++                    --ca-cert=$PKIDIR/testpki-cacert.pem \
++                    --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                    --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++                    list Connection], [0], [stdout], [ignore])
++
++# write access should fail
++AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
++                    --private-key=$PKIDIR/testpki-test-privkey.pem \
++                    --certificate=$PKIDIR/testpki-test-cert.pem \
++                    --ca-cert=$PKIDIR/testpki-cacert.pem \
++                    --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                    --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++                    chassis-add ch vxlan 1.2.4.8], [1], [ignore],
++[ovn-sbctl: transaction error: {"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"}
++])
++
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++AT_CLEANUP
++
++AT_SETUP([nb connection/ssl commands with ssl-ciphers and ssl-protocols])
++AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
++PKIDIR="$(cd $abs_top_builddir/tests && pwd)"
++AT_SKIP_IF([expr "$PKIDIR" : ".*[[ 	'\"
++\\]]"])
++
++: > .$1.db.~lock~
++ovsdb-tool create ovn-nb.db "$abs_top_srcdir"/ovn-nb.ovsschema
++
++# Start nb db server using db connection/ssl entries (unpopulated initially)
++start_daemon ovsdb-server --remote=punix:ovnnb_db.sock \
++                          --remote=db:OVN_Northbound,NB_Global,connections \
++                          --private-key=db:OVN_Northbound,SSL,private_key \
++                          --certificate=db:OVN_Northbound,SSL,certificate \
++                          --ca-cert=db:OVN_Northbound,SSL,ca_cert \
++                          --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                          --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++                          ovn-nb.db
++
++# Populate SSL configuration entries in nb db
++AT_CHECK(
++    [ovn-nbctl set-ssl $PKIDIR/testpki-test-privkey.pem \
++                       $PKIDIR/testpki-test-cert.pem \
++                       $PKIDIR/testpki-cacert.pem], [0], [stdout], [ignore])
++
++# Populate a passive SSL connection in nb db
++AT_CHECK([ovn-nbctl set-connection pssl:0:127.0.0.1], [0], [stdout], [ignore])
++
++PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
++
++# Verify SSL connetivity to nb db server
++AT_CHECK([ovn-nbctl --db=ssl:127.0.0.1:$TCP_PORT \
++                    --private-key=$PKIDIR/testpki-test-privkey.pem \
++                    --certificate=$PKIDIR/testpki-test-cert.pem \
++                    --ca-cert=$PKIDIR/testpki-cacert.pem \
++                    --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                    --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++          list NB_Global],
++         [0], [stdout], [ignore])
++AT_CHECK([ovn-nbctl --db=ssl:127.0.0.1:$TCP_PORT \
++                    --private-key=$PKIDIR/testpki-test-privkey.pem \
++                    --certificate=$PKIDIR/testpki-test-cert.pem \
++                    --ca-cert=$PKIDIR/testpki-cacert.pem \
++                    --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                    --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++          list Connection],
++         [0], [stdout], [ignore])
++AT_CHECK([ovn-nbctl --db=ssl:127.0.0.1:$TCP_PORT \
++                    --private-key=$PKIDIR/testpki-test-privkey.pem \
++                    --certificate=$PKIDIR/testpki-test-cert.pem \
++                    --ca-cert=$PKIDIR/testpki-cacert.pem \
++                    --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                    --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++          get-connection],
++         [0], [stdout], [ignore])
++
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++AT_CLEANUP
++
++AT_SETUP([sb connection/ssl commands with ssl-ciphers and ssl-protocols])
++AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
++PKIDIR="$(cd $abs_top_builddir/tests && pwd)"
++AT_SKIP_IF([expr "$PKIDIR" : ".*[[ 	'\"
++\\]]"])
++
++: > .$1.db.~lock~
++ovsdb-tool create ovn-sb.db "$abs_top_srcdir"/ovn-sb.ovsschema
++
++# Start sb db server using db connection/ssl entries (unpopulated initially)
++start_daemon ovsdb-server --remote=punix:ovnsb_db.sock \
++                          --remote=db:OVN_Southbound,SB_Global,connections \
++                          --private-key=db:OVN_Southbound,SSL,private_key \
++                          --certificate=db:OVN_Southbound,SSL,certificate \
++                          --ca-cert=db:OVN_Southbound,SSL,ca_cert \
++                          --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                          --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++                          ovn-sb.db
++
++# Populate SSL configuration entries in sb db
++AT_CHECK(
++    [ovn-sbctl set-ssl $PKIDIR/testpki-test-privkey.pem \
++                       $PKIDIR/testpki-test-cert.pem \
++                       $PKIDIR/testpki-cacert.pem], [0], [stdout], [ignore])
++
++# Populate a passive SSL connection in sb db
++AT_CHECK([ovn-sbctl set-connection pssl:0:127.0.0.1], [0], [stdout], [ignore])
++
++PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
++
++# Verify SSL connetivity to sb db server
++AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
++                    --private-key=$PKIDIR/testpki-test-privkey.pem \
++                    --certificate=$PKIDIR/testpki-test-cert.pem \
++                    --ca-cert=$PKIDIR/testpki-cacert.pem \
++                    --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                    --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++          list SB_Global],
++         [0], [stdout], [ignore])
++AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
++                    --private-key=$PKIDIR/testpki-test-privkey.pem \
++                    --certificate=$PKIDIR/testpki-test-cert.pem \
++                    --ca-cert=$PKIDIR/testpki-cacert.pem \
++                    --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                    --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++          list Connection],
++         [0], [stdout], [ignore])
++AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
++                    --private-key=$PKIDIR/testpki-test-privkey.pem \
++                    --certificate=$PKIDIR/testpki-test-cert.pem \
++                    --ca-cert=$PKIDIR/testpki-cacert.pem \
++                    --ssl-ciphers='HIGH:!aNULL:!MD5:@SECLEVEL=1' \
++                    --ssl-protocols='TLSv1,TLSv1.1,TLSv1.2' \
++          get-connection],
++         [0], [stdout], [ignore])
++
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++AT_CLEANUP
 diff --git a/tests/scapy-server.py b/tests/scapy-server.py
 new file mode 100755
 index 000000000..1cc616f70
@@ -8118,9 +11408,18 @@ index 000000000..1cc616f70
 +if __name__ == '__main__':
 +    main()
 diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at
-index 97c6e433b..65c4884ea 100644
+index 97c6e433b..2dfad32bb 100644
 --- a/tests/system-common-macros.at
 +++ b/tests/system-common-macros.at
+@@ -59,7 +59,7 @@ m4_define([ADD_BR], [ovs-vsctl _ADD_BR([$1]) -- $2])
+ # The existing 'port' or 'ovs-port' will be removed before new ones are added.
+ #
+ m4_define([ADD_VETH],
+-    [ AT_CHECK([ip link add $1 type veth peer name ovs-$1 || return 77])
++    [ AT_CHECK([ip link add $1 type veth peer name ovs-$1])
+       CONFIGURE_VETH_OFFLOADS([$1])
+       AT_CHECK([ip link set $1 netns $2])
+       AT_CHECK([ip link set dev ovs-$1 up])
 @@ -372,7 +372,7 @@ ADD_VETH(sw11, sw11, br-int, "192.168.2.2/24", "f0:00:00:02:02:03", \
           "192.168.2.1")
  ADD_NAMESPACES(server)
@@ -8141,7 +11440,7 @@ index 97c6e433b..65c4884ea 100644
  ovn-nbctl lsp-add public public1 \
          -- lsp-set-addresses public1 unknown \
 diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at
-index b29e6b55a..039d71170 100644
+index b29e6b55a..3a533d5e6 100644
 --- a/tests/system-ovn-kmod.at
 +++ b/tests/system-ovn-kmod.at
 @@ -1,224 +1,5 @@
@@ -8369,7 +11668,7 @@ index b29e6b55a..039d71170 100644
  OVN_FOR_EACH_NORTHD([
  AT_SETUP([load balancing affinity sessions - IPv4])
  AT_KEYWORDS([ovnlb])
-@@ -365,7 +146,7 @@ tcp,orig=(src=172.16.1.2,dst=172.16.1.100,sport=<cleared>,dport=<cleared>),reply
+@@ -365,13 +146,13 @@ tcp,orig=(src=172.16.1.2,dst=172.16.1.100,sport=<cleared>,dport=<cleared>),reply
  ])
  
  dp_key=$(printf "0x%x" $(fetch_column datapath tunnel_key external_ids:name=R2))
@@ -8378,6 +11677,14 @@ index b29e6b55a..039d71170 100644
   table=78, idle_timeout=60, tcp,metadata=$dp_key,nw_src=172.16.1.2,nw_dst=172.16.1.100,tp_dst=8080 actions=load:0x1->NXM_NX_REG10[[14]],load:0xc0a80<cleared>02->NXM_NX_REG4[[]],load:0x50->NXM_NX_REG8[[0..15]]
  ])
  
+ check_affinity_flows () {
+-n1=$(ovs-ofctl dump-flows br-int table=15 |awk '/priority=150,ct_state=\+new\+trk,ip,reg4=0xc0a80102/{print substr($4,11,length($4)-11)}')
+-n2=$(ovs-ofctl dump-flows br-int table=15 |awk '/priority=150,ct_state=\+new\+trk,ip,reg4=0xc0a80202/{print substr($4,11,length($4)-11)}')
++n1=$(ovs-ofctl dump-flows br-int table=15 |awk '/priority=150,ct_state=\+new\+trk,ip,reg4=0xc0a80102,.*nw_dst=172.16.1.100/{print substr($4,11,length($4)-11)}')
++n2=$(ovs-ofctl dump-flows br-int table=15 |awk '/priority=150,ct_state=\+new\+trk,ip,reg4=0xc0a80202,.*nw_dst=172.16.1.100/{print substr($4,11,length($4)-11)}')
+ [[ $n1 -gt 0 -a $n2 -eq 0 ]] || [[ $n1 -eq 0 -a $n2 -gt 0 ]]
+ echo $?
+ }
 @@ -588,31 +369,27 @@ ovn-nbctl lr-route-add R2 fd12::/64 fd20::1
  # Logical port 'foo1' in switch 'foo'.
  ADD_NAMESPACES(foo1)
@@ -8414,7 +11721,7 @@ index b29e6b55a..039d71170 100644
  ovn-nbctl lsp-add bar bar2 \
  -- lsp-set-addresses bar2 "e0:00:00:01:02:05 fd12::3"
  
-@@ -666,7 +443,7 @@ tcp,orig=(src=fd72::2,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd
+@@ -666,13 +443,13 @@ tcp,orig=(src=fd72::2,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd
  ])
  
  dp_key=$(printf "0x%x" $(fetch_column datapath tunnel_key external_ids:name=R2))
@@ -8423,7 +11730,15 @@ index b29e6b55a..039d71170 100644
   table=78, idle_timeout=60, tcp6,metadata=$dp_key,ipv6_src=fd72::2,ipv6_dst=fd30::1,tp_dst=8080 actions=load:0x1->NXM_NX_REG10[[14]],load:0x2->NXM_NX_XXREG1[[0..63]],load:0xfd1<cleared>000000000000->NXM_NX_XXREG1[[64..127]],load:0x50->NXM_NX_REG8[[0..15]]
  ])
  
-@@ -1115,3 +892,155 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+ check_affinity_flows () {
+-n1=$(ovs-ofctl dump-flows br-int table=15 |awk '/priority=150,ct_state=\+new\+trk,ipv6,reg4=0xfd110000/{print substr($4,11,length($4)-11)}')
+-n2=$(ovs-ofctl dump-flows br-int table=15 |awk '/priority=150,ct_state=\+new\+trk,ipv6,reg4=0xfd120000/{print substr($4,11,length($4)-11)}')
++n1=$(ovs-ofctl dump-flows br-int table=15 |awk '/priority=150,ct_state=\+new\+trk,ipv6,reg4=0xfd110000,.*ipv6_dst=fd30::1\s/{print substr($4,11,length($4)-11)}')
++n2=$(ovs-ofctl dump-flows br-int table=15 |awk '/priority=150,ct_state=\+new\+trk,ipv6,reg4=0xfd120000,.*ipv6_dst=fd30::1\s/{print substr($4,11,length($4)-11)}')
+ [[ $n1 -gt 0 -a $n2 -eq 0 ]] || [[ $n1 -eq 0 -a $n2 -gt 0 ]]
+ echo $?
+ }
+@@ -1115,3 +892,156 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
  /connection dropped.*/d"])
  AT_CLEANUP
  ])
@@ -8503,7 +11818,7 @@ index b29e6b55a..039d71170 100644
 +check ovn-nbctl set logical_router lr options:chassis=hv1
 +check ovn-nbctl set logical_router_port lr-internal options:gateway_mtu=800
 +
-+check ovn-nbctl lr-nat-add lr snat 192.168.1.1 172.16.1.2/24
++check ovn-nbctl lr-nat-add lr snat 192.168.1.1 172.16.1.2
 +
 +check ovn-nbctl --wait=hv sync
 +
@@ -8558,6 +11873,7 @@ index b29e6b55a..039d71170 100644
 +
 +dnl Expecting 2 outgoing packets and 2 fragments back - 8 lines total.
 +OVS_WAIT_UNTIL([test "$(cat client.tcpdump | wc -l)" = "8"])
++AT_CHECK([test $(grep -c "need to frag (mtu 800)" server.tcpdump) -eq 1])
 +
 +ovn-appctl -t ovn-controller vlog/set info
 +
@@ -8580,7 +11896,7 @@ index b29e6b55a..039d71170 100644
 +AT_CLEANUP
 +])
 diff --git a/tests/system-ovn.at b/tests/system-ovn.at
-index 59d0cb2a0..3ecf1db42 100644
+index 59d0cb2a0..213483176 100644
 --- a/tests/system-ovn.at
 +++ b/tests/system-ovn.at
 @@ -251,24 +251,21 @@ ovn-nbctl lr-route-add R2 fd12::/64 fd00::1
@@ -8863,7 +12179,65 @@ index 59d0cb2a0..3ecf1db42 100644
  
  # Wait for ovn-controller to catch up.
  wait_for_ports_up
-@@ -6306,8 +6272,7 @@ NS_CHECK_EXEC([alice1], [sysctl -w net.ipv6.conf.default.router_solicitations=1]
+@@ -6145,6 +6111,10 @@ on_exit 'ovs-ofctl dump-flows br-int'
+ 
+ NETNS_DAEMONIZE([alice1], [nc -l -k 80], [alice1.pid])
+ NS_CHECK_EXEC([bob1], [nc -z 10.0.0.2 80], [0])
++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
++])
+ 
+ # Ensure conntrack entry is present. We should not try to predict
+ # the tunnel key for the output port, so we strip it from the labels
+@@ -6152,17 +6122,19 @@ NS_CHECK_EXEC([bob1], [nc -z 10.0.0.2 80], [0])
+ 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>/'], [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.
++ovs-appctl dpctl/dump-flows | grep 'ct_state(+new-est-rpl+trk).*ct(.*label=0x401020400000000/.*)'
+ AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(+new-est-rpl+trk).*ct(.*label=0x401020400000000/.*)' -c], [0], [dnl
+-1
++2
+ ])
+ AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+rpl+trk).*ct_label(0x401020400000000)' -c], [0], [dnl
+-1
++2
+ ])
+ 
+ # Flush conntrack entries for easier output parsing of next test.
+@@ -6176,16 +6148,21 @@ ovn-nbctl set Logical_Switch_Port r2-ext \
+ ovn-nbctl --wait=hv sync
+ 
+ NS_CHECK_EXEC([bob1], [nc -z 10.0.0.2 80], [0])
++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 | grep 'ct_state(+new-est-rpl+trk).*ct(.*label=0x1001020400000000/.*)' -c], [0], [dnl
+-1
++2
+ ])
+ AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+rpl+trk).*ct_label(0x1001020400000000)' -c], [0], [dnl
+-1
++2
+ ])
+ 
+ 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>/'], [0], [dnl
++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
+@@ -6306,8 +6283,7 @@ NS_CHECK_EXEC([alice1], [sysctl -w net.ipv6.conf.default.router_solicitations=1]
  net.ipv6.conf.default.router_solicitations = 1
  ])
  ADD_VETH(alice1, alice1, br-int, "fd01::2/64", "f0:00:00:01:02:04", \
@@ -8873,7 +12247,7 @@ index 59d0cb2a0..3ecf1db42 100644
  ovn-nbctl lsp-add alice alice1 \
  -- lsp-set-addresses alice1 "f0:00:00:01:02:04 fd01::2"
  # Add neighbour MAC address to avoid sending IPv6 NS messages which could
-@@ -6322,8 +6287,7 @@ NS_CHECK_EXEC([bob1], [sysctl -w net.ipv6.conf.default.router_solicitations=1],
+@@ -6322,8 +6298,7 @@ NS_CHECK_EXEC([bob1], [sysctl -w net.ipv6.conf.default.router_solicitations=1],
  net.ipv6.conf.default.router_solicitations = 1
  ])
  ADD_VETH(bob1, bob1, br-int, "fd07::1/64", "f0:00:00:01:02:06", \
@@ -8883,7 +12257,65 @@ index 59d0cb2a0..3ecf1db42 100644
  # Add neighbour MAC addresses to avoid sending IPv6 NS messages which could
  # cause datapath flows to be evicted
  NS_CHECK_EXEC([bob1], [ip -6 neigh add fd07::2 lladdr 00:00:02:01:02:03 dev bob1], [0])
-@@ -6513,8 +6477,6 @@ ADD_NAMESPACES(ext-foo)
+@@ -6339,16 +6314,20 @@ on_exit 'ovs-ofctl dump-flows br-int'
+ 
+ NETNS_DAEMONIZE([alice1], [nc -6 -l -k 80], [alice1.pid])
+ NS_CHECK_EXEC([bob1], [nc -6 -z fd01::2 80], [0])
++NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 fd01::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
+ 
+ # 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 | grep 'ct_state(+new-est-rpl+trk).*ct(.*label=0x401020400000000/.*)' -c], [0], [dnl
+-1
++2
+ ])
+ 
+ AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+rpl+trk).*ct_label(0x401020400000000)' -c], [0], [dnl
+-1
++2
+ ])
+ 
+ # Ensure conntrack entry is present. We should not try to predict
+@@ -6356,7 +6335,8 @@ AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+rpl+trk).*ct_lab
+ # 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>/'], [0], [dnl
++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>)
+ ])
+ 
+@@ -6369,17 +6349,22 @@ ovn-nbctl --wait=hv set Logical_Switch_Port r2-ext \
+      type=router options:router-port=R2_ext addresses='"00:00:10:01:02:04"'
+ 
+ NS_CHECK_EXEC([bob1], [nc -6 -z fd01::2 80], [0])
++NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 fd01::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
+ 
+ AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(+new-est-rpl+trk).*ct(.*label=0x1001020400000000/.*)' -c], [0], [dnl
+-1
++2
+ ])
+ AT_CHECK([ovs-appctl dpctl/dump-flows | grep 'ct_state(-new+est+rpl+trk).*ct_label(0x1001020400000000)' -c], [0], [dnl
+-1
++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>/'], [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>)
+ ])
+ 
+@@ -6513,8 +6498,6 @@ 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")
  
@@ -8892,7 +12324,7 @@ index 59d0cb2a0..3ecf1db42 100644
  AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext])
  ovn-nbctl lsp-add public public1 \
          -- lsp-set-addresses public1 unknown \
-@@ -6630,6 +6592,28 @@ AT_CHECK([ovn-nbctl set Logical_Switch_Port ext options:qos_min_rate=400000])
+@@ -6630,6 +6613,28 @@ AT_CHECK([ovn-nbctl set Logical_Switch_Port ext options:qos_min_rate=400000])
  AT_CHECK([ovn-nbctl set Logical_Switch_Port ext options:qos_max_rate=600000])
  AT_CHECK([ovn-nbctl set Logical_Switch_Port ext options:qos_burst=6000000])
  
@@ -8921,7 +12353,7 @@ index 59d0cb2a0..3ecf1db42 100644
  OVS_WAIT_UNTIL([tc qdisc show | grep -q 'htb 1: dev ovs-public'])
  OVS_WAIT_UNTIL([tc class show dev ovs-public | \
                  grep -q 'class htb .* rate 200Kbit ceil 300Kbit burst 375000b cburst 375000b'])
-@@ -7348,9 +7332,9 @@ check ovn-nbctl lsp-add public public1 \
+@@ -7348,9 +7353,9 @@ check ovn-nbctl lsp-add public public1 \
  
  NS_EXEC([sw01], [tcpdump -l -n -i sw01 icmp -Q in > reject.pcap &])
  check ovn-nbctl meter-add acl-meter drop 1 pktps 0
@@ -8934,7 +12366,7 @@ index 59d0cb2a0..3ecf1db42 100644
  
  AT_CHECK([ovn-nbctl copp-list copp0], [0], [dnl
  reject: acl-meter
-@@ -7371,7 +7355,7 @@ rm -f reject.pcap
+@@ -7371,7 +7376,7 @@ rm -f reject.pcap
  
  # Let's update the meter
  NS_EXEC([sw01], [tcpdump -l -n -i sw01 icmp -Q in > reject.pcap &])
@@ -8943,7 +12375,7 @@ index 59d0cb2a0..3ecf1db42 100644
  ip netns exec sw01 scapy -H <<-EOF
  p = IP(src="192.168.1.2", dst="192.168.1.1") / UDP(dport = 12345) / Raw(b"X"*64)
  send (p, iface='sw01', loop = 0, verbose = 0, count = 40)
-@@ -7402,7 +7386,7 @@ kill $(pidof tcpdump)
+@@ -7402,7 +7407,7 @@ kill $(pidof tcpdump)
  
  NS_EXEC([server], [tcpdump -l -n -i s1 arp[[24:4]]=0xac100164 > arp.pcap &])
  check ovn-nbctl meter-add arp-meter drop 1 pktps 0
@@ -8952,7 +12384,7 @@ index 59d0cb2a0..3ecf1db42 100644
  check ovn-nbctl --wait=hv lr-copp-add copp1 R1
  AT_CHECK([ovn-nbctl copp-list copp1], [0], [dnl
  arp-resolve: arp-meter
-@@ -7421,7 +7405,7 @@ OVS_WAIT_UNTIL([
+@@ -7421,7 +7426,7 @@ OVS_WAIT_UNTIL([
  kill $(pidof tcpdump)
  
  check ovn-nbctl meter-add icmp-meter drop 1 pktps 0
@@ -8961,7 +12393,7 @@ index 59d0cb2a0..3ecf1db42 100644
  check ovn-nbctl --wait=hv lr-copp-add copp2 R1
  AT_CHECK([ovn-nbctl copp-list copp2 |grep icmp4-error], [0], [dnl
  icmp4-error: icmp-meter
-@@ -7441,7 +7425,7 @@ OVS_WAIT_UNTIL([
+@@ -7441,7 +7446,7 @@ OVS_WAIT_UNTIL([
  kill $(pidof tcpdump)
  
  check ovn-nbctl meter-add bfd-meter drop 1 pktps 0
@@ -8970,7 +12402,7 @@ index 59d0cb2a0..3ecf1db42 100644
  check ovn-nbctl --wait=hv lr-copp-add copp3 R1
  AT_CHECK([ovn-nbctl copp-list copp3 |grep bfd], [0], [dnl
  bfd: bfd-meter
-@@ -7471,7 +7455,7 @@ kill $(pidof tcpdump)
+@@ -7471,7 +7476,7 @@ kill $(pidof tcpdump)
  
  check ovn-nbctl set nb_global . options:svc_monitor_mac="33:33:33:33:33:33"
  check ovn-nbctl meter-add svc-meter drop 1 pktps 0
@@ -8979,7 +12411,7 @@ index 59d0cb2a0..3ecf1db42 100644
  check ovn-nbctl --wait=hv ls-copp-add copp4 sw0
  check ovn-appctl -t ovn-controller vlog/set vconn:dbg
  AT_CHECK([ovn-nbctl copp-list copp4], [0], [dnl
-@@ -8696,22 +8680,19 @@ check ovn-nbctl lr-nat-add lr1 snat 172.16.1.10 192.168.1.0/24
+@@ -8696,22 +8701,19 @@ check ovn-nbctl lr-nat-add lr1 snat 172.16.1.10 192.168.1.0/24
  check ovn-nbctl lr-nat-add lr1 snat 1711::10 2001::/64
  
  ADD_NAMESPACES(ls1p1)
@@ -9008,7 +12440,7 @@ index 59d0cb2a0..3ecf1db42 100644
  
  NS_CHECK_EXEC([ls1p1], [ping -q -c 3 -i 0.3 -w 2  172.16.1.1 | FORMAT_PING], \
  [0], [dnl
-@@ -9231,8 +9212,9 @@ name: 'vport4' value: '999'
+@@ -9231,8 +9233,9 @@ name: 'vport4' value: '999'
  NETNS_DAEMONIZE([vm1], [nc -k -l 42.42.42.2 4242], [nc-vm1.pid])
  
  NETNS_DAEMONIZE([vm1],
@@ -9019,7 +12451,7 @@ index 59d0cb2a0..3ecf1db42 100644
  
  # Make sure connecting to the VIP works (hairpin, via ls and via lr).
  NS_CHECK_EXEC([vm1], [nc 66.66.66.66 666 -z], [0], [ignore], [ignore])
-@@ -9355,16 +9337,13 @@ check ovn-nbctl --template lb-add lb-test-udp2 "^vip:^vport4" "[[4242::2]]:4343"
+@@ -9355,16 +9358,13 @@ check ovn-nbctl --template lb-add lb-test-udp2 "^vip:^vport4" "[[4242::2]]:4343"
      -- lr-lb-add rtr lb-test-udp2
  
  ADD_NAMESPACES(vm1)
@@ -9039,7 +12471,7 @@ index 59d0cb2a0..3ecf1db42 100644
  
  # Wait for ovn-controller to catch up.
  wait_for_ports_up
-@@ -9385,8 +9364,9 @@ name: 'vport4' value: '999'
+@@ -9385,8 +9385,9 @@ name: 'vport4' value: '999'
  NETNS_DAEMONIZE([vm1], [nc -k -l 4242::2 4242], [nc-vm1.pid])
  
  NETNS_DAEMONIZE([vm1],
@@ -9050,7 +12482,7 @@ index 59d0cb2a0..3ecf1db42 100644
  
  # Make sure connecting to the VIP works (hairpin, via ls and via lr).
  NS_CHECK_EXEC([vm1], [nc 6666::1 666 -z], [0], [ignore], [ignore])
-@@ -10825,32 +10805,28 @@ check ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
+@@ -10825,32 +10826,28 @@ check ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
  # Logical port 'foo1' in switch 'foo'.
  ADD_NAMESPACES(foo1)
  ADD_VETH(foo1, foo1, br-int, "fd11::2/64", "f0:00:00:01:02:03", \
@@ -9087,7 +12519,7 @@ index 59d0cb2a0..3ecf1db42 100644
  check ovn-nbctl lsp-add bar bar1 \
  -- lsp-set-addresses bar1 "f0:00:00:01:02:06 fd12::2"
  
-@@ -11686,7 +11662,7 @@ check ovn-nbctl lsp-add ls0 ls0-lr0 \
+@@ -11686,7 +11683,7 @@ check ovn-nbctl lsp-add ls0 ls0-lr0 \
      -- lsp-set-options ls0-lr0 router-port=lr0-ls0
  
  ADD_NAMESPACES(vif0)
@@ -9096,7 +12528,7 @@ index 59d0cb2a0..3ecf1db42 100644
  OVS_WAIT_UNTIL([test "$(ip netns exec vif0 ip a | grep fe80:: | grep tentative)" = ""])
  
  check ovn-nbctl set logical_router lr0 options:always_learn_from_arp_request=false
-@@ -11729,3 +11705,417 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+@@ -11729,3 +11726,497 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
  
  AT_CLEANUP
  ])
@@ -9427,6 +12859,86 @@ index 59d0cb2a0..3ecf1db42 100644
 +AT_CLEANUP
 +
 +OVN_FOR_EACH_NORTHD([
++AT_SETUP([load balancing in gateway router - client behind LB with SNAT])
++AT_SKIP_IF([test $HAVE_NC = no])
++AT_KEYWORDS([lb])
++
++ovn_start
++OVS_TRAFFIC_VSWITCHD_START()
++ADD_BR([br-int])
++
++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_daemon ovn-controller
++
++check ovn-nbctl lr-add lr \
++    -- set logical_router lr options:chassis=hv1
++check ovn-nbctl lrp-add lr lr-ls1 00:00:00:00:01:00 41.41.41.2/24
++check ovn-nbctl lrp-add lr lr-ls2 00:00:00:00:02:00 42.42.42.2/24
++check ovn-nbctl ls-add ls1
++check ovn-nbctl ls-add ls2
++
++check ovn-nbctl lsp-add ls1 ls1-lr
++check ovn-nbctl lsp-set-addresses ls1-lr 00:00:00:00:01:00
++check ovn-nbctl lsp-set-type ls1-lr router
++check ovn-nbctl lsp-set-options ls1-lr router-port=lr-ls1
++check ovn-nbctl lsp-add ls1 vm1
++check ovn-nbctl lsp-set-addresses vm1 00:00:00:00:00:01
++
++check ovn-nbctl lsp-add ls2 ls2-lr
++check ovn-nbctl lsp-set-addresses ls2-lr 00:00:00:00:02:00
++check ovn-nbctl lsp-set-type ls2-lr router
++check ovn-nbctl lsp-set-options ls2-lr router-port=lr-ls2
++check ovn-nbctl lsp-add ls2 vm2
++check ovn-nbctl lsp-set-addresses vm2 00:00:00:00:00:02
++
++dnl LB using the router IP connected to vm2 as VIP.
++check ovn-nbctl lb-add lb-test 42.42.42.2:8080 41.41.41.1:8080 tcp \
++    -- lr-lb-add lr lb-test
++
++dnl SNAT everything coming from vm1 to the router IP (towards vm2).
++check ovn-nbctl lr-nat-add lr snat 42.42.42.2 41.41.41.1
++
++ADD_NAMESPACES(vm1)
++ADD_VETH(vm1, vm1, br-int, "41.41.41.1/24", "00:00:00:00:00:01", "41.41.41.2")
++
++ADD_NAMESPACES(vm2)
++ADD_VETH(vm2, vm2, br-int, "42.42.42.1/24", "00:00:00:00:00:02", "42.42.42.2")
++
++dnl Start a server on vm2.
++NETNS_DAEMONIZE([vm2], [nc -l -k 42.42.42.1 80], [vm2.pid])
++
++dnl Wait for ovn-controller to catch up.
++wait_for_ports_up
++check ovn-nbctl --wait=hv sync
++
++dnl Test the connection originating something that uses the same source port
++dnl as the LB VIP.
++NS_CHECK_EXEC([vm1], [nc -z -p 8080 42.42.42.1 80], 0, [ignore], [ignore])
++
++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(["/failed to query port patch-.*/d
++/connection dropped.*/d"])
++AT_CLEANUP
++])
++
++OVN_FOR_EACH_NORTHD([
 +AT_SETUP([load balancing affinity sessions - auto clear learnt flows])
 +AT_SKIP_IF([test $HAVE_NC = no])
 +AT_KEYWORDS([lb])
@@ -9793,6 +13305,25 @@ index 82804096f..01f4aa26b 100644
      <p><code>--db-ic-nb-cluster-local-addr=<var>IP ADDRESS</var></code></p>
      <p><code>--db-ic-nb-cluster-local-port=<var>PORT NUMBER</var></code></p>
      <p><code>--db-ic-nb-cluster-local-proto=<var>PROTO (tcp/ssl)</var></code></p>
+diff --git a/utilities/ovn-dbctl.c b/utilities/ovn-dbctl.c
+index 2e9348c47..92be27b2c 100644
+--- a/utilities/ovn-dbctl.c
++++ b/utilities/ovn-dbctl.c
+@@ -610,6 +610,14 @@ apply_options_direct(const struct ovn_dbctl_options *dbctl_options,
+             ssl_ca_cert_file = optarg;
+             break;
+ 
++        case OPT_SSL_PROTOCOLS:
++            stream_ssl_set_protocols(optarg);
++            break;
++
++        case OPT_SSL_CIPHERS:
++            stream_ssl_set_ciphers(optarg);
++            break;
++
+         case OPT_BOOTSTRAP_CA_CERT:
+             stream_ssl_set_ca_cert_file(po->arg, true);
+             break;
 diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
 index 444fbd2fe..821ad44dd 100644
 --- a/utilities/ovn-nbctl.c
@@ -9854,7 +13385,7 @@ index 3948cae3f..f1f8c2b42 100644
  
          ds_put_cstr(&ctx->output, "\n  vips:\n");
 diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
-index 0b86eae7b..13ae464ad 100644
+index 0b86eae7b..e0f1c3ec9 100644
 --- a/utilities/ovn-trace.c
 +++ b/utilities/ovn-trace.c
 @@ -983,6 +983,7 @@ parse_lflow_for_datapath(const struct sbrec_logical_flow *sblf,
@@ -9865,3 +13396,12 @@ index 0b86eae7b..13ae464ad 100644
              free(error);
              return;
          }
+@@ -2462,7 +2463,7 @@ execute_ct_nat(const struct ovnact_ct_nat *ct_nat,
+ }
+ 
+ static void
+-execute_ct_commit_nat(const struct ovnact_ct_nat *ct_nat,
++execute_ct_commit_nat(const struct ovnact_ct_commit_nat *ct_nat,
+                       const struct ovntrace_datapath *dp, struct flow *uflow,
+                       enum ovnact_pipeline pipeline, struct ovs_list *super)
+ {
diff --git a/SPECS/ovn23.09.spec b/SPECS/ovn23.09.spec
index 6299c18..608dc84 100644
--- a/SPECS/ovn23.09.spec
+++ b/SPECS/ovn23.09.spec
@@ -51,7 +51,7 @@ Summary: Open Virtual Network support
 Group: System Environment/Daemons
 URL: http://www.ovn.org/
 Version: 23.09.0
-Release: 103%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
+Release: 136%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
 Provides: openvswitch%{pkgver}-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release}
 Obsoletes: openvswitch%{pkgver}-ovn-common < 2.11.0-1
 
@@ -64,8 +64,8 @@ License: ASL 2.0 and LGPLv2+ and SISSL
 # 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 ec1d730163d984934c467e050ebf6d39f8c09384
-%define ovsshortcommit ec1d730
+%define ovscommit 49e64f13b2c965f5b53a65eeab70ac2e3f0bf69a
+%define ovsshortcommit 49e64f1
 
 Source10: https://github.com/openvswitch/ovs/archive/%{ovscommit}.tar.gz#/openvswitch-%{ovsshortcommit}.tar.gz
 %define ovsdir ovs-%{ovscommit}
@@ -127,7 +127,9 @@ BuildRequires: tcpdump
 BuildRequires: libcap-ng libcap-ng-devel
 %endif
 
+%if 0%{?rhel} == 9
 BuildRequires: python3-scapy
+%endif
 
 Requires: hostname openssl iproute module-init-tools
 
@@ -528,6 +530,138 @@ fi
 %{_unitdir}/ovn-controller-vtep.service
 
 %changelog
+* Mon Mar 11 2024 Frode Nordahl <frode.nordahl@canonical.com> - 23.09.0-136
+- controller: Set check_tnl_key for BFD on tunnel ifaces.
+[Upstream: c966c35f1b1cd8c5351ccac3051843fbf765c2ae]
+
+* Fri Mar 08 2024 Dumitru Ceara <dceara@redhat.com> - 23.09.0-135
+- tests: Skip EDNS test if the scapy version doesn't support it.
+[Upstream: 7af89a5e50a4ba75a3ea5c393499f1e0fa0a6abb]
+
+* Wed Mar 06 2024 Dumitru Ceara <dceara@redhat.com> - 23.09.0-134
+- northd: Don't skip the unSNAT stage for traffic towards VIPs.
+[Upstream: 094b1217345a8ae5935fdd4dfec4949f46197377]
+
+* Fri Mar 01 2024 Mark Michelson <mmichels@redhat.com> - 23.09.0-133
+- Prepare for 23.09.3.
+[Upstream: 7bd52d7a25f2ddad0be25a5e54a3eb63d98a19d8]
+
+* Fri Mar 01 2024 Mark Michelson <mmichels@redhat.com> - 23.09.0-132
+- Set release date for 23.09.2.
+[Upstream: 04b23938302ad54f453f622a4b0c2fa5e27d3e41]
+
+* Thu Feb 29 2024 Ilya Maximets <i.maximets@ovn.org> - 23.09.0-131
+- northd: Don't create fair Sb meters for ACLs with logging disabled.
+[Upstream: 215d53ea1436f03ab26a1a65df0824b319e6a4c3]
+
+* Wed Feb 28 2024 Mohammad Heib <mheib@redhat.com> - 23.09.0-130
+- ci: Update crun in GitHub actions runner.
+[Upstream: 5bf1773c90ef7b61a85946027a987184e8d74fa0]
+
+* Wed Feb 28 2024 Ales Musil <amusil@redhat.com> - 23.09.0-129
+- ci: Update crun in Cirrus CI cloud image.
+[Upstream: afa3da7677ed4d484612b820d8f09642d5821bd4]
+
+* Tue Feb 27 2024 Ilya Maximets <i.maximets@ovn.org> - 23.09.0-128
+- controller: ofctrl: Use index for meter lookups.
+[Upstream: 683fb6dd2fc3c2ab025b1dd87ba2883e40d6d775]
+
+* Mon Feb 19 2024 Xavier Simonart <xsimonar@redhat.com> - 23.09.0-127
+- tests: Fix "router port type update and then ...".
+[Upstream: c463d1de1a0c2cd368a4809f0d9eda9792b79851]
+
+* Mon Feb 19 2024 Xavier Simonart <xsimonar@redhat.com> - 23.09.0-126
+- tests: Fix "ovn-controller - Chassis other_config".
+[Upstream: cbd4f2fcd0223a96c739dd07eded753f8f9b2a30]
+
+* Mon Feb 19 2024 Xavier Simonart <xsimonar@redhat.com> - 23.09.0-125
+- tests: Fix "ofctrl wait before clearing flows".
+[Upstream: 81486b62bcac0d081ca907533ae34d826605b485]
+
+* Mon Feb 19 2024 Xavier Simonart <xsimonar@redhat.com> - 23.09.0-124
+- tests: Fix flaky "ovn-controller-vtep - binding 1".
+[Upstream: 48a08a447340b095e8472d40aaaac5156320b4c1]
+
+* Mon Feb 19 2024 Xavier Simonart <xsimonar@redhat.com> - 23.09.0-123
+- tests: Fix flaky "options:requested-chassis ...".
+[Upstream: a088df5aa75a7207ccdd751d2167e1536113737f]
+
+* Mon Feb 19 2024 Xavier Simonart <xsimonar@redhat.com> - 23.09.0-122
+- tests: Fix typos in tests.
+[Upstream: 0a5726652b202add51d1dc8b6557268673e6cc51]
+
+* Mon Feb 19 2024 Xavier Simonart <xsimonar@redhat.com> - 23.09.0-121
+- tests: Have tests fail when adding veth peer fails.
+[Upstream: 609a943e33c734d368f2019e7d3b41e31bb31d6f]
+
+* Mon Feb 12 2024 Dumitru Ceara <dceara@redhat.com> - 23.09.0-120
+- pinctrl: dns: Ignore additional records.
+[Upstream: 511f5a214226be84ae3b9434ffcab973e37295eb]
+
+* Mon Feb 12 2024 Roberto Bartzen Acosta <roberto.acosta@luizalabs.com> - 23.09.0-119
+- ovn-ic: Fix global blacklist filter for IPv6 addresses.
+[Upstream: 27d23712260b9faba23018ce973010743e30ccf7]
+
+* Mon Feb 12 2024 Xavier Simonart <xsimonar@redhat.com> - 23.09.0-118
+- tests: Fix macro OVN_CHECK_PACKETS_CONTAIN.
+[Upstream: 28b0eddff68c5a64b80071a9a27cb79e3fac792a]
+
+* Fri Feb 09 2024 Mark Michelson <mmichels@redhat.com> - 23.09.0-117
+- features.c: Always wait on the rconn.
+[Upstream: c0c9e507470439c3220b99c361f71e0cff3406fc]
+
+* Fri Feb 09 2024 Ales Musil <amusil@redhat.com> - 23.09.0-116
+- ci: Bump CirrusCI Ubuntu image version
+[Upstream: 41e7f01872dae61b9ffcc1d3871865313ff90619]
+
+* Fri Feb 09 2024 Nobuhiro MIKI <nmiki@yahoo-corp.jp> - 23.09.0-115
+- Documentation: Fix broken links in ovn-sandbox.rst.
+[Upstream: 99d22a176f45971516803129f08c7a37a50bc4a1]
+
+* Fri Feb 09 2024 Mark Michelson <mmichels@redhat.com> - 23.09.0-114
+- ovn-sb.xml: Remove IPv4-only restriction from Service Monitors.
+[Upstream: 97fca0f846bf6839144fc04fed6f0873198b4f89]
+
+* Fri Feb 09 2024 Ilya Maximets <i.maximets@ovn.org> - 23.09.0-113
+- github: Update versions of action dependencies (Node.js 20).
+[Upstream: 2981936b61e0e0694c16df979b986dd1cb60b147]
+
+* Thu Feb 08 2024 Ales Musil <amusil@redhat.com> - 23.09.0-112
+- northd: Remove the protocol match from ECMP symmetric reply flows.
+[Upstream: a36f2955be67a6581e81fb3ae27de825e0046b52]
+
+* Thu Feb 08 2024 Ales Musil <amusil@redhat.com> - 23.09.0-111
+- northd: Explicitly handle SNAT for ICMP need frag.
+[Upstream: 6a4c412f43d5f1c076fac3784a4ffeb8a3861436]
+
+* Thu Feb 08 2024 Ales Musil <amusil@redhat.com> - 23.09.0-110
+- actions: Adjust the ct_commit_nat action.
+[Upstream: 069842478601c0b01b0cc3117637e5a00344fcb6]
+
+* Tue Jan 30 2024 Dumitru Ceara <dceara@redhat.com> - 23.09.0-109
+- ovs: Bump submodule to tip of OVS branch-3.2.
+[Upstream: f224c6e5f69c099ddb008f99dba2e19a902a612f]
+
+* Tue Jan 30 2024 Dumitru Ceara <dceara@redhat.com> - 23.09.0-108
+- actions: Use random port selection for SNAT with external_port_range.
+[Upstream: 7ee483a45df19e11e26487e64a93940e0de64b9a]
+
+* Tue Jan 30 2024 Mohammad Heib <mheib@redhat.com> - 23.09.0-107
+- ovn-ic: Handle NB:name updates properly.
+[Upstream: 0e684ec206e8979694912ad1037145ccd0d0b7dc]
+
+* Thu Jan 25 2024 Ales Musil <amusil@redhat.com> - 23.09.0-106
+- northd: Make sure that affinity flows match on VIP.
+[Upstream: 859e8d917408d50272c910f78ac44ab8a593aa13]
+
+* Wed Jan 24 2024 Aliasgar Ginwala <aginwala@ebay.com> - 23.09.0-105
+- Fix segfault due to ssl-ciphers.
+[Upstream: d39e7c0068ecc719a3d6154e2078d6d9a3435fc9]
+
+* Wed Jan 24 2024 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 23.09.0-104
+- ovn: Add tunnel PMTUD support. (#2241711)
+[Upstream: 6d2f9d60760a793c15ca7423b24ff586b653fc76]
+
 * Tue Jan 23 2024 Xavier Simonart <xsimonar@redhat.com> - 23.09.0-103
 - controller: fixed potential segfault when changing tunnel_key and deleting ls.
 [Upstream: 120075357a624293d52a1905c47a1bd249d2157c]