diff mbox series

[ovs-dev,v3,2/2] northd: Explicitly handle SNAT for ICMP need frag.

Message ID 20240129062023.7059-3-amusil@redhat.com
State Superseded
Headers show
Series [ovs-dev,v3,1/2] actions: Adjust the ct_commit_nat action. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes success github build: passed

Commit Message

Ales Musil Jan. 29, 2024, 6:20 a.m. UTC
Considering following topology:
client - sw0 - lrp0 - lr - lrp1 - sw1 - server
sw0 in subnet 192.168.0.0/24
sw1 in subnet 172.168.0.0/24
SNAT configured for client
gateway_mtu=1400 configured for lrp0

If we send UDP traffic from client to server
and server responds with packet bigger than 1400
the following sequence will happen:

1) Packet is coming into lr via lrp1
2) unSNAT
3) Routing, the outport will be set to lrp0
4) Check for packet larger will fail
5) We will generate ICMP need frag

However, the last step is wrong from the server
perspective. The ICMP message will have IP source
address = lrp1 IP address. Which means that SNAT won't
happen because the source is not within the sw0 subnet,
but the inner packet has sw0 subnet address, because it
was unSNATted. This results in server ignoring the ICMP
message because server never sent any packet to the
sw0 subnet.

In order to prevent this issue perform SNAT for the
ICMP packet. Because the packet is related to already
existing connection we just need to perform
ct_commit_nat(snat) action.

This is achieved with addition of the following flow for
"lr_in_larger_pkts" stage (the flow for IPv6 is the in
regard to the addition):

match=(inport == "INPORT" && outport == "OUTPORT" && ip4 && REGBIT_PKT_LARGER && REGBIT_EGRESS_LOOPBACK == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; REGBIT_EGRESS_LOOPBACK = 1; REGBIT_PKT_LARGER = 0; eth.dst = ETH_DST; ip4.dst = ip4.src; ip4.src = IP_SRC; 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); };)

Also, add flow to "lr_out_post_snat" stage:

match=(icmp && flags.icmp_snat == 1), action=(ct_commit_nat(snat);)

Partially revert 0e49f49c73d6 ("northd: Allow need frag to be SNATed")
which attempted to fix the same issue in a wrong way.

Also add feature flag for the updated ct_commit_nat action.
In case there is an update of northd to newer version before all
controllers are updated.

Fixes: 0e49f49c73d6 ("northd: Allow need frag to be SNATed")
Reported-at: https://issues.redhat.com/browse/FDP-134
Reported-at: https://issues.redhat.com/browse/FDP-159
Signed-off-by: Ales Musil <amusil@redhat.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
---
v3: Rebase on top of current main.
v2: Rebase on top of current main.
    Squash the 2/3 and 3/3 from previous version to single commit.
    Add ack from Dumitru.
---
 controller/chassis.c         |   8 ++
 include/ovn/features.h       |   1 +
 include/ovn/logical-fields.h |   3 +
 lib/logical-fields.c         |   4 +
 northd/northd.c              | 192 ++++++++++++++++++++---------------
 northd/northd.h              |   1 +
 tests/ovn-northd.at          | 118 ++++++++++++++-------
 tests/ovn.at                 |   6 +-
 tests/system-ovn-kmod.at     |   3 +-
 9 files changed, 214 insertions(+), 122 deletions(-)

Comments

Dumitru Ceara Jan. 29, 2024, 9:36 a.m. UTC | #1
On 1/29/24 07:20, Ales Musil wrote:
> Considering following topology:
> client - sw0 - lrp0 - lr - lrp1 - sw1 - server
> sw0 in subnet 192.168.0.0/24
> sw1 in subnet 172.168.0.0/24
> SNAT configured for client
> gateway_mtu=1400 configured for lrp0
> 
> If we send UDP traffic from client to server
> and server responds with packet bigger than 1400
> the following sequence will happen:
> 
> 1) Packet is coming into lr via lrp1
> 2) unSNAT
> 3) Routing, the outport will be set to lrp0
> 4) Check for packet larger will fail
> 5) We will generate ICMP need frag
> 
> However, the last step is wrong from the server
> perspective. The ICMP message will have IP source
> address = lrp1 IP address. Which means that SNAT won't
> happen because the source is not within the sw0 subnet,
> but the inner packet has sw0 subnet address, because it
> was unSNATted. This results in server ignoring the ICMP
> message because server never sent any packet to the
> sw0 subnet.
> 
> In order to prevent this issue perform SNAT for the
> ICMP packet. Because the packet is related to already
> existing connection we just need to perform
> ct_commit_nat(snat) action.
> 
> This is achieved with addition of the following flow for
> "lr_in_larger_pkts" stage (the flow for IPv6 is the in
> regard to the addition):
> 
> match=(inport == "INPORT" && outport == "OUTPORT" && ip4 && REGBIT_PKT_LARGER && REGBIT_EGRESS_LOOPBACK == 0 && ct.trk && ct.rpl && ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; REGBIT_EGRESS_LOOPBACK = 1; REGBIT_PKT_LARGER = 0; eth.dst = ETH_DST; ip4.dst = ip4.src; ip4.src = IP_SRC; 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); };)
> 
> Also, add flow to "lr_out_post_snat" stage:
> 
> match=(icmp && flags.icmp_snat == 1), action=(ct_commit_nat(snat);)
> 
> Partially revert 0e49f49c73d6 ("northd: Allow need frag to be SNATed")
> which attempted to fix the same issue in a wrong way.
> 
> Also add feature flag for the updated ct_commit_nat action.
> In case there is an update of northd to newer version before all
> controllers are updated.
> 
> Fixes: 0e49f49c73d6 ("northd: Allow need frag to be SNATed")
> Reported-at: https://issues.redhat.com/browse/FDP-134
> Reported-at: https://issues.redhat.com/browse/FDP-159
> Signed-off-by: Ales Musil <amusil@redhat.com>
> Acked-by: Dumitru Ceara <dceara@redhat.com>
> ---
> v3: Rebase on top of current main.
> v2: Rebase on top of current main.
>     Squash the 2/3 and 3/3 from previous version to single commit.
>     Add ack from Dumitru.
> ---

Hi Ales,

Before accepting this patch I'd like to try to clarify one thing that
was flagged as a potential issue by Numan (ct.dnat), please see below.

>  controller/chassis.c         |   8 ++
>  include/ovn/features.h       |   1 +
>  include/ovn/logical-fields.h |   3 +
>  lib/logical-fields.c         |   4 +
>  northd/northd.c              | 192 ++++++++++++++++++++---------------
>  northd/northd.h              |   1 +
>  tests/ovn-northd.at          | 118 ++++++++++++++-------
>  tests/ovn.at                 |   6 +-
>  tests/system-ovn-kmod.at     |   3 +-
>  9 files changed, 214 insertions(+), 122 deletions(-)
> 
> diff --git a/controller/chassis.c b/controller/chassis.c
> index a6f13ccc4..ba2e57238 100644
> --- a/controller/chassis.c
> +++ b/controller/chassis.c
> @@ -370,6 +370,7 @@ chassis_build_other_config(const struct ovs_chassis_cfg *ovs_cfg,
>      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");
>  }
>  
>  /*
> @@ -509,6 +510,12 @@ chassis_other_config_changed(const struct ovs_chassis_cfg *ovs_cfg,
>          return true;
>      }
>  
> +    if (!smap_get_bool(&chassis_rec->other_config,
> +                       OVN_FEATURE_CT_COMMIT_NAT_V2,
> +                       false)) {
> +        return true;
> +    }
> +
>      return false;
>  }
>  
> @@ -640,6 +647,7 @@ update_supported_sset(struct sset *supported)
>      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
> diff --git a/include/ovn/features.h b/include/ovn/features.h
> index 2c47ab766..08f1d8288 100644
> --- a/include/ovn/features.h
> +++ b/include/ovn/features.h
> @@ -27,6 +27,7 @@
>  #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.
> diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
> index e8e0e6d33..f07c4c42e 100644
> --- a/include/ovn/logical-fields.h
> +++ b/include/ovn/logical-fields.h
> @@ -79,6 +79,7 @@ enum mff_log_flags_bits {
>      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 */
> @@ -132,6 +133,8 @@ enum mff_log_flags {
>  
>      /* 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/logical-fields.c b/lib/logical-fields.c
> index 662c1ef0e..20219a67a 100644
> --- a/lib/logical-fields.c
> +++ b/lib/logical-fields.c
> @@ -132,6 +132,10 @@ ovn_init_symtab(struct shash *symtab)
>      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);
> diff --git a/northd/northd.c b/northd/northd.c
> index bb028f2ef..222143220 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -501,6 +501,15 @@ build_chassis_features(const struct sbrec_chassis_table *sbrec_chassis_table,
>              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;
> +        }
>      }
>  }
>  
> @@ -14132,96 +14141,103 @@ build_arp_resolve_flows_for_lsp(
>      }
>  }
>  
> +#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
> -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;
> +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;
> +    }
>  
> -    char *ip4_src = NULL;
> +    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,
> -                      outport ? outport_match : "");
> +    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));
>  
> -        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, 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,
> -                                  copp_meter_get(
> -                                        COPP_ICMP4_ERR,
> -                                        op->od->nbr->copp,
> -                                        meter_groups),
> -                                  &op->nbrp->header_);
> -    }
> +    ovn_lflow_add_with_hint__(lflows, op->od, stage, priority,
> +                              ds_cstr(match), ds_cstr(actions),
> +                              NULL, meter, &op->nbrp->header_);
>  
> -    char *ip6_src = NULL;
> +    ds_truncate(match, match_len);
> +}
>  
> -    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;
> -    }
> +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)
> +{
> +    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);
>  
> -    if (ip6_src) {
> -        ds_clear(match);
> -        ds_put_format(match, "inport == %s && %sip6 && "REGBIT_PKT_LARGER
> -                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
> -                      outport ? outport_match : "");
> +    ds_clear(match);
> +    ds_put_format(match, "inport == %s", op->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, 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,
> -                                  copp_meter_get(
> -                                        COPP_ICMP6_ERR,
> -                                        op->od->nbr->copp,
> -                                        meter_groups),
> -                                  &op->nbrp->header_);
> +    if (outport) {
> +        ds_put_format(match, " && outport == %s", outport->json_key);
> +
> +        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; ");

Numan pointed out offline that in the past we tried to avoid matching on
"ct.dnat" because it creates flows that cannot be offloaded to hardware
(for specific vendors) [0].  However, these flows we add here always
send packets to ovn-controller (pinctrl) and cannot be offloaded anyway.

To me that seems to be fine but I'd like to hear an opinion from Numan
and Han about this too.

Regards,
Dumitru

[0]
https://github.com/ovn-org/ovn/commit/110e670462a9ae580d40d14181c388fc2f0b1e1c

>      }
> -    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
> @@ -14229,8 +14245,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) {
> @@ -14259,6 +14275,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.
> @@ -14279,7 +14301,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);
>  
> @@ -14296,7 +14319,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);
>      }
>  }
>  
> @@ -16266,7 +16289,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,
> @@ -17898,6 +17921,7 @@ northd_enable_all_features(struct northd_data *data)
>          .ct_lb_related = true,
>          .fdb_timestamp = true,
>          .ls_dpg_column = true,
> +        .ct_commit_nat_v2 = true,
>      };
>  }
>  
> diff --git a/northd/northd.h b/northd/northd.h
> index ebbb4ba0a..27ae5da08 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -71,6 +71,7 @@ struct chassis_features {
>      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
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 18073ad19..ba8c67e4e 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -6138,7 +6138,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
> @@ -6183,10 +6184,14 @@ 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 = 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-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=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
> @@ -6201,6 +6206,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
>  
> @@ -6214,10 +6223,14 @@ 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 = 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-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=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
> @@ -6232,6 +6245,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
>  
> @@ -6243,10 +6260,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-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 = 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-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=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
> @@ -6268,14 +6289,22 @@ 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 = 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); };)
> +  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=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
> @@ -6294,6 +6323,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
>  
> @@ -6307,14 +6340,22 @@ 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 = 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); };)
> +  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=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
> @@ -6333,16 +6374,19 @@ 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 = 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); };)
> +  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=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
> @@ -6375,6 +6419,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
>  ])
>  
> diff --git a/tests/ovn.at b/tests/ovn.at
> index d362b4c56..2411c653e 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -20601,12 +20601,12 @@ test_ip_packet_larger() {
>          expected=${expected}0000000000000000000000000000
>          echo $expected > br_phys_n1.expected
>      else
> -        src_ip=`ip_to_hex 172.168.0.100`
> +        src_ip=`ip_to_hex 10 0 0 1`
>          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}00004000fe01c55f
> +        icmp_reply=${src_mac}${dst_mac}08004500${reply_pkt_len}00004000fe01686b
>          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}
> @@ -20698,7 +20698,7 @@ test_ip6_packet_larger() {
>  
>      local ipv6_src=10000000000000000000000000000003
>      local ipv6_dst=20000000000000000000000000000002
> -    local ipv6_rt=20000000000000000000000000000001
> +    local ipv6_rt=10000000000000000000000000000001
>  
>      local payload=0000000000000000000000000000000000000000
>      local payload=${payload}0000000000000000000000000000000000000000
> diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at
> index 88c72edcd..2341bb387 100644
> --- a/tests/system-ovn-kmod.at
> +++ b/tests/system-ovn-kmod.at
> @@ -977,7 +977,7 @@ dnl check ovn-nbctl lr-lb-add lr lb1
>  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
>  
> @@ -1032,6 +1032,7 @@ NS_CHECK_EXEC([client], [$PYTHON3 ./client.py])
>  
>  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
>
Han Zhou Feb. 2, 2024, 1:30 a.m. UTC | #2
On Mon, Jan 29, 2024 at 1:36 AM Dumitru Ceara <dceara@redhat.com> wrote:
>
> On 1/29/24 07:20, Ales Musil wrote:
> > Considering following topology:
> > client - sw0 - lrp0 - lr - lrp1 - sw1 - server
> > sw0 in subnet 192.168.0.0/24
> > sw1 in subnet 172.168.0.0/24
> > SNAT configured for client
> > gateway_mtu=1400 configured for lrp0
> >
> > If we send UDP traffic from client to server
> > and server responds with packet bigger than 1400
> > the following sequence will happen:
> >
> > 1) Packet is coming into lr via lrp1
> > 2) unSNAT
> > 3) Routing, the outport will be set to lrp0
> > 4) Check for packet larger will fail
> > 5) We will generate ICMP need frag
> >
> > However, the last step is wrong from the server
> > perspective. The ICMP message will have IP source
> > address = lrp1 IP address. Which means that SNAT won't
> > happen because the source is not within the sw0 subnet,
> > but the inner packet has sw0 subnet address, because it
> > was unSNATted. This results in server ignoring the ICMP
> > message because server never sent any packet to the
> > sw0 subnet.
> >
> > In order to prevent this issue perform SNAT for the
> > ICMP packet. Because the packet is related to already
> > existing connection we just need to perform
> > ct_commit_nat(snat) action.
> >
> > This is achieved with addition of the following flow for
> > "lr_in_larger_pkts" stage (the flow for IPv6 is the in
> > regard to the addition):
> >
> > match=(inport == "INPORT" && outport == "OUTPORT" && ip4 &&
REGBIT_PKT_LARGER && REGBIT_EGRESS_LOOPBACK == 0 && ct.trk && ct.rpl &&
ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; REGBIT_EGRESS_LOOPBACK
= 1; REGBIT_PKT_LARGER = 0; eth.dst = ETH_DST; ip4.dst = ip4.src; ip4.src =
IP_SRC; 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); };)
> >
> > Also, add flow to "lr_out_post_snat" stage:
> >
> > match=(icmp && flags.icmp_snat == 1), action=(ct_commit_nat(snat);)
> >
> > Partially revert 0e49f49c73d6 ("northd: Allow need frag to be SNATed")
> > which attempted to fix the same issue in a wrong way.
> >
> > Also add feature flag for the updated ct_commit_nat action.
> > In case there is an update of northd to newer version before all
> > controllers are updated.
> >
> > Fixes: 0e49f49c73d6 ("northd: Allow need frag to be SNATed")
> > Reported-at: https://issues.redhat.com/browse/FDP-134
> > Reported-at: https://issues.redhat.com/browse/FDP-159
> > Signed-off-by: Ales Musil <amusil@redhat.com>
> > Acked-by: Dumitru Ceara <dceara@redhat.com>
> > ---
> > v3: Rebase on top of current main.
> > v2: Rebase on top of current main.
> >     Squash the 2/3 and 3/3 from previous version to single commit.
> >     Add ack from Dumitru.
> > ---
>
> Hi Ales,
>
> Before accepting this patch I'd like to try to clarify one thing that
> was flagged as a potential issue by Numan (ct.dnat), please see below.
>
> >  controller/chassis.c         |   8 ++
> >  include/ovn/features.h       |   1 +
> >  include/ovn/logical-fields.h |   3 +
> >  lib/logical-fields.c         |   4 +
> >  northd/northd.c              | 192 ++++++++++++++++++++---------------
> >  northd/northd.h              |   1 +
> >  tests/ovn-northd.at          | 118 ++++++++++++++-------
> >  tests/ovn.at                 |   6 +-
> >  tests/system-ovn-kmod.at     |   3 +-
> >  9 files changed, 214 insertions(+), 122 deletions(-)
> >
> > diff --git a/controller/chassis.c b/controller/chassis.c
> > index a6f13ccc4..ba2e57238 100644
> > --- a/controller/chassis.c
> > +++ b/controller/chassis.c
> > @@ -370,6 +370,7 @@ chassis_build_other_config(const struct
ovs_chassis_cfg *ovs_cfg,
> >      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");
> >  }
> >
> >  /*
> > @@ -509,6 +510,12 @@ chassis_other_config_changed(const struct
ovs_chassis_cfg *ovs_cfg,
> >          return true;
> >      }
> >
> > +    if (!smap_get_bool(&chassis_rec->other_config,
> > +                       OVN_FEATURE_CT_COMMIT_NAT_V2,
> > +                       false)) {
> > +        return true;
> > +    }
> > +
> >      return false;
> >  }
> >
> > @@ -640,6 +647,7 @@ update_supported_sset(struct sset *supported)
> >      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
> > diff --git a/include/ovn/features.h b/include/ovn/features.h
> > index 2c47ab766..08f1d8288 100644
> > --- a/include/ovn/features.h
> > +++ b/include/ovn/features.h
> > @@ -27,6 +27,7 @@
> >  #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.
> > diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
> > index e8e0e6d33..f07c4c42e 100644
> > --- a/include/ovn/logical-fields.h
> > +++ b/include/ovn/logical-fields.h
> > @@ -79,6 +79,7 @@ enum mff_log_flags_bits {
> >      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 */
> > @@ -132,6 +133,8 @@ enum mff_log_flags {
> >
> >      /* 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/logical-fields.c b/lib/logical-fields.c
> > index 662c1ef0e..20219a67a 100644
> > --- a/lib/logical-fields.c
> > +++ b/lib/logical-fields.c
> > @@ -132,6 +132,10 @@ ovn_init_symtab(struct shash *symtab)
> >      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);
> > diff --git a/northd/northd.c b/northd/northd.c
> > index bb028f2ef..222143220 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -501,6 +501,15 @@ build_chassis_features(const struct
sbrec_chassis_table *sbrec_chassis_table,
> >              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;
> > +        }
> >      }
> >  }
> >
> > @@ -14132,96 +14141,103 @@ build_arp_resolve_flows_for_lsp(
> >      }
> >  }
> >
> > +#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
> > -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;
> > +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;
> > +    }
> >
> > -    char *ip4_src = NULL;
> > +    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,
> > -                      outport ? outport_match : "");
> > +    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));
> >
> > -        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, 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,
> > -                                  copp_meter_get(
> > -                                        COPP_ICMP4_ERR,
> > -                                        op->od->nbr->copp,
> > -                                        meter_groups),
> > -                                  &op->nbrp->header_);
> > -    }
> > +    ovn_lflow_add_with_hint__(lflows, op->od, stage, priority,
> > +                              ds_cstr(match), ds_cstr(actions),
> > +                              NULL, meter, &op->nbrp->header_);
> >
> > -    char *ip6_src = NULL;
> > +    ds_truncate(match, match_len);
> > +}
> >
> > -    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;
> > -    }
> > +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)
> > +{
> > +    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);
> >
> > -    if (ip6_src) {
> > -        ds_clear(match);
> > -        ds_put_format(match, "inport == %s && %sip6 &&
"REGBIT_PKT_LARGER
> > -                      " && "REGBIT_EGRESS_LOOPBACK" == 0",
op->json_key,
> > -                      outport ? outport_match : "");
> > +    ds_clear(match);
> > +    ds_put_format(match, "inport == %s", op->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, 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,
> > -                                  copp_meter_get(
> > -                                        COPP_ICMP6_ERR,
> > -                                        op->od->nbr->copp,
> > -                                        meter_groups),
> > -                                  &op->nbrp->header_);
> > +    if (outport) {
> > +        ds_put_format(match, " && outport == %s", outport->json_key);
> > +
> > +        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; ");
>
> Numan pointed out offline that in the past we tried to avoid matching on
> "ct.dnat" because it creates flows that cannot be offloaded to hardware
> (for specific vendors) [0].  However, these flows we add here always
> send packets to ovn-controller (pinctrl) and cannot be offloaded anyway.
>
> To me that seems to be fine but I'd like to hear an opinion from Numan
> and Han about this too.
>
> Regards,
> Dumitru
>
> [0]
>
https://github.com/ovn-org/ovn/commit/110e670462a9ae580d40d14181c388fc2f0b1e1c
>
Thanks Dumitru for double checking this. If I understand correctly, the
ct.dnat match is only in the flows in the table lr_in_larger_pkts, which
are only looked at when a packet is already categorized as "too large". So
for normal packets the lookup should not generate DP flows with ct.dnat (or
!ct.dnat), so it shouldn't impact HW offload in general. Of course larger
packets would always go through slow path as you mentioned.

Regards,
Han

>
> >      }
> > -    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
> > @@ -14229,8 +14245,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) {
> > @@ -14259,6 +14275,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.
> > @@ -14279,7 +14301,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);
> >
> > @@ -14296,7 +14319,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);
> >      }
> >  }
> >
> > @@ -16266,7 +16289,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,
> > @@ -17898,6 +17921,7 @@ northd_enable_all_features(struct northd_data
*data)
> >          .ct_lb_related = true,
> >          .fdb_timestamp = true,
> >          .ls_dpg_column = true,
> > +        .ct_commit_nat_v2 = true,
> >      };
> >  }
> >
> > diff --git a/northd/northd.h b/northd/northd.h
> > index ebbb4ba0a..27ae5da08 100644
> > --- a/northd/northd.h
> > +++ b/northd/northd.h
> > @@ -71,6 +71,7 @@ struct chassis_features {
> >      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
> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > index 18073ad19..ba8c67e4e 100644
> > --- a/tests/ovn-northd.at
> > +++ b/tests/ovn-northd.at
> > @@ -6138,7 +6138,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
> > @@ -6183,10 +6184,14 @@ 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 = 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-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=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
> > @@ -6201,6 +6206,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
> >
> > @@ -6214,10 +6223,14 @@ 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 = 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-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=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
> > @@ -6232,6 +6245,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
> >
> > @@ -6243,10 +6260,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-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 = 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-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=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
> > @@ -6268,14 +6289,22 @@ 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 = 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); };)
> > +  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=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
> > @@ -6294,6 +6323,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
> >
> > @@ -6307,14 +6340,22 @@ 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 = 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); };)
> > +  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=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
> > @@ -6333,16 +6374,19 @@ 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 = 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); };)
> > +  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=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
> > @@ -6375,6 +6419,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
> >  ])
> >
> > diff --git a/tests/ovn.at b/tests/ovn.at
> > index d362b4c56..2411c653e 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -20601,12 +20601,12 @@ test_ip_packet_larger() {
> >          expected=${expected}0000000000000000000000000000
> >          echo $expected > br_phys_n1.expected
> >      else
> > -        src_ip=`ip_to_hex 172.168.0.100`
> > +        src_ip=`ip_to_hex 10 0 0 1`
> >          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}00004000fe01c55f
> > +
 icmp_reply=${src_mac}${dst_mac}08004500${reply_pkt_len}00004000fe01686b
> >
 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}
> > @@ -20698,7 +20698,7 @@ test_ip6_packet_larger() {
> >
> >      local ipv6_src=10000000000000000000000000000003
> >      local ipv6_dst=20000000000000000000000000000002
> > -    local ipv6_rt=20000000000000000000000000000001
> > +    local ipv6_rt=10000000000000000000000000000001
> >
> >      local payload=0000000000000000000000000000000000000000
> >      local payload=${payload}0000000000000000000000000000000000000000
> > diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at
> > index 88c72edcd..2341bb387 100644
> > --- a/tests/system-ovn-kmod.at
> > +++ b/tests/system-ovn-kmod.at
> > @@ -977,7 +977,7 @@ dnl check ovn-nbctl lr-lb-add lr lb1
> >  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
> >
> > @@ -1032,6 +1032,7 @@ NS_CHECK_EXEC([client], [$PYTHON3 ./client.py])
> >
> >  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
> >
Han Zhou Feb. 2, 2024, 1:32 a.m. UTC | #3
On Thu, Feb 1, 2024 at 5:30 PM Han Zhou <hzhou@ovn.org> wrote:
>
>
>
> On Mon, Jan 29, 2024 at 1:36 AM Dumitru Ceara <dceara@redhat.com> wrote:
> >
> > On 1/29/24 07:20, Ales Musil wrote:
> > > Considering following topology:
> > > client - sw0 - lrp0 - lr - lrp1 - sw1 - server
> > > sw0 in subnet 192.168.0.0/24
> > > sw1 in subnet 172.168.0.0/24
> > > SNAT configured for client
> > > gateway_mtu=1400 configured for lrp0
> > >
> > > If we send UDP traffic from client to server
> > > and server responds with packet bigger than 1400
> > > the following sequence will happen:
> > >
> > > 1) Packet is coming into lr via lrp1
> > > 2) unSNAT
> > > 3) Routing, the outport will be set to lrp0
> > > 4) Check for packet larger will fail
> > > 5) We will generate ICMP need frag
> > >
> > > However, the last step is wrong from the server
> > > perspective. The ICMP message will have IP source
> > > address = lrp1 IP address. Which means that SNAT won't
> > > happen because the source is not within the sw0 subnet,
> > > but the inner packet has sw0 subnet address, because it
> > > was unSNATted. This results in server ignoring the ICMP
> > > message because server never sent any packet to the
> > > sw0 subnet.
> > >
> > > In order to prevent this issue perform SNAT for the
> > > ICMP packet. Because the packet is related to already
> > > existing connection we just need to perform
> > > ct_commit_nat(snat) action.
> > >
> > > This is achieved with addition of the following flow for
> > > "lr_in_larger_pkts" stage (the flow for IPv6 is the in
> > > regard to the addition):
> > >
> > > match=(inport == "INPORT" && outport == "OUTPORT" && ip4 &&
REGBIT_PKT_LARGER && REGBIT_EGRESS_LOOPBACK == 0 && ct.trk && ct.rpl &&
ct.dnat), action=(icmp4_error {flags.icmp_snat = 1; REGBIT_EGRESS_LOOPBACK
= 1; REGBIT_PKT_LARGER = 0; eth.dst = ETH_DST; ip4.dst = ip4.src; ip4.src =
IP_SRC; 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); };)
> > >
> > > Also, add flow to "lr_out_post_snat" stage:
> > >
> > > match=(icmp && flags.icmp_snat == 1), action=(ct_commit_nat(snat);)
> > >
> > > Partially revert 0e49f49c73d6 ("northd: Allow need frag to be SNATed")
> > > which attempted to fix the same issue in a wrong way.
> > >
> > > Also add feature flag for the updated ct_commit_nat action.
> > > In case there is an update of northd to newer version before all
> > > controllers are updated.
> > >
> > > Fixes: 0e49f49c73d6 ("northd: Allow need frag to be SNATed")
> > > Reported-at: https://issues.redhat.com/browse/FDP-134
> > > Reported-at: https://issues.redhat.com/browse/FDP-159
> > > Signed-off-by: Ales Musil <amusil@redhat.com>
> > > Acked-by: Dumitru Ceara <dceara@redhat.com>
> > > ---
> > > v3: Rebase on top of current main.
> > > v2: Rebase on top of current main.
> > >     Squash the 2/3 and 3/3 from previous version to single commit.
> > >     Add ack from Dumitru.
> > > ---
> >
> > Hi Ales,
> >
> > Before accepting this patch I'd like to try to clarify one thing that
> > was flagged as a potential issue by Numan (ct.dnat), please see below.
> >
> > >  controller/chassis.c         |   8 ++
> > >  include/ovn/features.h       |   1 +
> > >  include/ovn/logical-fields.h |   3 +
> > >  lib/logical-fields.c         |   4 +
> > >  northd/northd.c              | 192
++++++++++++++++++++---------------
> > >  northd/northd.h              |   1 +
> > >  tests/ovn-northd.at          | 118 ++++++++++++++-------
> > >  tests/ovn.at                 |   6 +-
> > >  tests/system-ovn-kmod.at     |   3 +-
> > >  9 files changed, 214 insertions(+), 122 deletions(-)
> > >
> > > diff --git a/controller/chassis.c b/controller/chassis.c
> > > index a6f13ccc4..ba2e57238 100644
> > > --- a/controller/chassis.c
> > > +++ b/controller/chassis.c
> > > @@ -370,6 +370,7 @@ chassis_build_other_config(const struct
ovs_chassis_cfg *ovs_cfg,
> > >      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");
> > >  }
> > >
> > >  /*
> > > @@ -509,6 +510,12 @@ chassis_other_config_changed(const struct
ovs_chassis_cfg *ovs_cfg,
> > >          return true;
> > >      }
> > >
> > > +    if (!smap_get_bool(&chassis_rec->other_config,
> > > +                       OVN_FEATURE_CT_COMMIT_NAT_V2,
> > > +                       false)) {
> > > +        return true;
> > > +    }
> > > +
> > >      return false;
> > >  }
> > >
> > > @@ -640,6 +647,7 @@ update_supported_sset(struct sset *supported)
> > >      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
> > > diff --git a/include/ovn/features.h b/include/ovn/features.h
> > > index 2c47ab766..08f1d8288 100644
> > > --- a/include/ovn/features.h
> > > +++ b/include/ovn/features.h
> > > @@ -27,6 +27,7 @@
> > >  #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.
> > > diff --git a/include/ovn/logical-fields.h
b/include/ovn/logical-fields.h
> > > index e8e0e6d33..f07c4c42e 100644
> > > --- a/include/ovn/logical-fields.h
> > > +++ b/include/ovn/logical-fields.h
> > > @@ -79,6 +79,7 @@ enum mff_log_flags_bits {
> > >      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 */
> > > @@ -132,6 +133,8 @@ enum mff_log_flags {
> > >
> > >      /* 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/logical-fields.c b/lib/logical-fields.c
> > > index 662c1ef0e..20219a67a 100644
> > > --- a/lib/logical-fields.c
> > > +++ b/lib/logical-fields.c
> > > @@ -132,6 +132,10 @@ ovn_init_symtab(struct shash *symtab)
> > >      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);
> > > diff --git a/northd/northd.c b/northd/northd.c
> > > index bb028f2ef..222143220 100644
> > > --- a/northd/northd.c
> > > +++ b/northd/northd.c
> > > @@ -501,6 +501,15 @@ build_chassis_features(const struct
sbrec_chassis_table *sbrec_chassis_table,
> > >              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;
> > > +        }
> > >      }
> > >  }
> > >
> > > @@ -14132,96 +14141,103 @@ build_arp_resolve_flows_for_lsp(
> > >      }
> > >  }
> > >
> > > +#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
> > > -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;
> > > +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;
> > > +    }
> > >
> > > -    char *ip4_src = NULL;
> > > +    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,
> > > -                      outport ? outport_match : "");
> > > +    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));
> > >
> > > -        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, 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,
> > > -                                  copp_meter_get(
> > > -                                        COPP_ICMP4_ERR,
> > > -                                        op->od->nbr->copp,
> > > -                                        meter_groups),
> > > -                                  &op->nbrp->header_);
> > > -    }
> > > +    ovn_lflow_add_with_hint__(lflows, op->od, stage, priority,
> > > +                              ds_cstr(match), ds_cstr(actions),
> > > +                              NULL, meter, &op->nbrp->header_);
> > >
> > > -    char *ip6_src = NULL;
> > > +    ds_truncate(match, match_len);
> > > +}
> > >
> > > -    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;
> > > -    }
> > > +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)
> > > +{
> > > +    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);
> > >
> > > -    if (ip6_src) {
> > > -        ds_clear(match);
> > > -        ds_put_format(match, "inport == %s && %sip6 &&
"REGBIT_PKT_LARGER
> > > -                      " && "REGBIT_EGRESS_LOOPBACK" == 0",
op->json_key,
> > > -                      outport ? outport_match : "");
> > > +    ds_clear(match);
> > > +    ds_put_format(match, "inport == %s", op->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, 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,
> > > -                                  copp_meter_get(
> > > -                                        COPP_ICMP6_ERR,
> > > -                                        op->od->nbr->copp,
> > > -                                        meter_groups),
> > > -                                  &op->nbrp->header_);
> > > +    if (outport) {
> > > +        ds_put_format(match, " && outport == %s", outport->json_key);
> > > +
> > > +        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; ");
> >
> > Numan pointed out offline that in the past we tried to avoid matching on
> > "ct.dnat" because it creates flows that cannot be offloaded to hardware
> > (for specific vendors) [0].  However, these flows we add here always
> > send packets to ovn-controller (pinctrl) and cannot be offloaded anyway.
> >
> > To me that seems to be fine but I'd like to hear an opinion from Numan
> > and Han about this too.
> >
> > Regards,
> > Dumitru
> >
> > [0]
> >
https://github.com/ovn-org/ovn/commit/110e670462a9ae580d40d14181c388fc2f0b1e1c
> >
> Thanks Dumitru for double checking this. If I understand correctly, the
ct.dnat match is only in the flows in the table lr_in_larger_pkts, which
are only looked at when a packet is already categorized as "too large". So
for normal packets the lookup should not generate DP flows with ct.dnat (or
!ct.dnat), so it shouldn't impact HW offload in general. Of course larger
packets would always go through slow path as you mentioned.

If we want to verify this, it would be good to run some regular traffic
(including north-south with DNAT/SNAT) and make sure there is
no ct.dnat involved in any DP flows.

Thanks,
Han

>
> Regards,
> Han
>
> >
> > >      }
> > > -    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
> > > @@ -14229,8 +14245,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) {
> > > @@ -14259,6 +14275,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.
> > > @@ -14279,7 +14301,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);
> > >
> > > @@ -14296,7 +14319,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);
> > >      }
> > >  }
> > >
> > > @@ -16266,7 +16289,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,
> > > @@ -17898,6 +17921,7 @@ northd_enable_all_features(struct northd_data
*data)
> > >          .ct_lb_related = true,
> > >          .fdb_timestamp = true,
> > >          .ls_dpg_column = true,
> > > +        .ct_commit_nat_v2 = true,
> > >      };
> > >  }
> > >
> > > diff --git a/northd/northd.h b/northd/northd.h
> > > index ebbb4ba0a..27ae5da08 100644
> > > --- a/northd/northd.h
> > > +++ b/northd/northd.h
> > > @@ -71,6 +71,7 @@ struct chassis_features {
> > >      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
> > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > > index 18073ad19..ba8c67e4e 100644
> > > --- a/tests/ovn-northd.at
> > > +++ b/tests/ovn-northd.at
> > > @@ -6138,7 +6138,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
> > > @@ -6183,10 +6184,14 @@ 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 = 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-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=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
> > > @@ -6201,6 +6206,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
> > >
> > > @@ -6214,10 +6223,14 @@ 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 = 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-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=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
> > > @@ -6232,6 +6245,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
> > >
> > > @@ -6243,10 +6260,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-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 = 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-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=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
> > > @@ -6268,14 +6289,22 @@ 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 = 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); };)
> > > +  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=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
> > > @@ -6294,6 +6323,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
> > >
> > > @@ -6307,14 +6340,22 @@ 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 = 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); };)
> > > +  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=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
> > > @@ -6333,16 +6374,19 @@ 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 = 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); };)
> > > +  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=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
> > > @@ -6375,6 +6419,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
> > >  ])
> > >
> > > diff --git a/tests/ovn.at b/tests/ovn.at
> > > index d362b4c56..2411c653e 100644
> > > --- a/tests/ovn.at
> > > +++ b/tests/ovn.at
> > > @@ -20601,12 +20601,12 @@ test_ip_packet_larger() {
> > >          expected=${expected}0000000000000000000000000000
> > >          echo $expected > br_phys_n1.expected
> > >      else
> > > -        src_ip=`ip_to_hex 172.168.0.100`
> > > +        src_ip=`ip_to_hex 10 0 0 1`
> > >          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}00004000fe01c55f
> > > +
 icmp_reply=${src_mac}${dst_mac}08004500${reply_pkt_len}00004000fe01686b
> > >
 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}
> > > @@ -20698,7 +20698,7 @@ test_ip6_packet_larger() {
> > >
> > >      local ipv6_src=10000000000000000000000000000003
> > >      local ipv6_dst=20000000000000000000000000000002
> > > -    local ipv6_rt=20000000000000000000000000000001
> > > +    local ipv6_rt=10000000000000000000000000000001
> > >
> > >      local payload=0000000000000000000000000000000000000000
> > >      local payload=${payload}0000000000000000000000000000000000000000
> > > diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at
> > > index 88c72edcd..2341bb387 100644
> > > --- a/tests/system-ovn-kmod.at
> > > +++ b/tests/system-ovn-kmod.at
> > > @@ -977,7 +977,7 @@ dnl check ovn-nbctl lr-lb-add lr lb1
> > >  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
> > >
> > > @@ -1032,6 +1032,7 @@ NS_CHECK_EXEC([client], [$PYTHON3 ./client.py])
> > >
> > >  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
> > >

>
diff mbox series

Patch

diff --git a/controller/chassis.c b/controller/chassis.c
index a6f13ccc4..ba2e57238 100644
--- a/controller/chassis.c
+++ b/controller/chassis.c
@@ -370,6 +370,7 @@  chassis_build_other_config(const struct ovs_chassis_cfg *ovs_cfg,
     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");
 }
 
 /*
@@ -509,6 +510,12 @@  chassis_other_config_changed(const struct ovs_chassis_cfg *ovs_cfg,
         return true;
     }
 
+    if (!smap_get_bool(&chassis_rec->other_config,
+                       OVN_FEATURE_CT_COMMIT_NAT_V2,
+                       false)) {
+        return true;
+    }
+
     return false;
 }
 
@@ -640,6 +647,7 @@  update_supported_sset(struct sset *supported)
     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
diff --git a/include/ovn/features.h b/include/ovn/features.h
index 2c47ab766..08f1d8288 100644
--- a/include/ovn/features.h
+++ b/include/ovn/features.h
@@ -27,6 +27,7 @@ 
 #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.
diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
index e8e0e6d33..f07c4c42e 100644
--- a/include/ovn/logical-fields.h
+++ b/include/ovn/logical-fields.h
@@ -79,6 +79,7 @@  enum mff_log_flags_bits {
     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 */
@@ -132,6 +133,8 @@  enum mff_log_flags {
 
     /* 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/logical-fields.c b/lib/logical-fields.c
index 662c1ef0e..20219a67a 100644
--- a/lib/logical-fields.c
+++ b/lib/logical-fields.c
@@ -132,6 +132,10 @@  ovn_init_symtab(struct shash *symtab)
     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);
diff --git a/northd/northd.c b/northd/northd.c
index bb028f2ef..222143220 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -501,6 +501,15 @@  build_chassis_features(const struct sbrec_chassis_table *sbrec_chassis_table,
             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;
+        }
     }
 }
 
@@ -14132,96 +14141,103 @@  build_arp_resolve_flows_for_lsp(
     }
 }
 
+#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
-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;
+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;
+    }
 
-    char *ip4_src = NULL;
+    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,
-                      outport ? outport_match : "");
+    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));
 
-        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, 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,
-                                  copp_meter_get(
-                                        COPP_ICMP4_ERR,
-                                        op->od->nbr->copp,
-                                        meter_groups),
-                                  &op->nbrp->header_);
-    }
+    ovn_lflow_add_with_hint__(lflows, op->od, stage, priority,
+                              ds_cstr(match), ds_cstr(actions),
+                              NULL, meter, &op->nbrp->header_);
 
-    char *ip6_src = NULL;
+    ds_truncate(match, match_len);
+}
 
-    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;
-    }
+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)
+{
+    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);
 
-    if (ip6_src) {
-        ds_clear(match);
-        ds_put_format(match, "inport == %s && %sip6 && "REGBIT_PKT_LARGER
-                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
-                      outport ? outport_match : "");
+    ds_clear(match);
+    ds_put_format(match, "inport == %s", op->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, 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,
-                                  copp_meter_get(
-                                        COPP_ICMP6_ERR,
-                                        op->od->nbr->copp,
-                                        meter_groups),
-                                  &op->nbrp->header_);
+    if (outport) {
+        ds_put_format(match, " && outport == %s", outport->json_key);
+
+        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
@@ -14229,8 +14245,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) {
@@ -14259,6 +14275,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.
@@ -14279,7 +14301,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);
 
@@ -14296,7 +14319,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);
     }
 }
 
@@ -16266,7 +16289,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,
@@ -17898,6 +17921,7 @@  northd_enable_all_features(struct northd_data *data)
         .ct_lb_related = true,
         .fdb_timestamp = true,
         .ls_dpg_column = true,
+        .ct_commit_nat_v2 = true,
     };
 }
 
diff --git a/northd/northd.h b/northd/northd.h
index ebbb4ba0a..27ae5da08 100644
--- a/northd/northd.h
+++ b/northd/northd.h
@@ -71,6 +71,7 @@  struct chassis_features {
     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
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 18073ad19..ba8c67e4e 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -6138,7 +6138,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
@@ -6183,10 +6184,14 @@  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 = 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-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=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
@@ -6201,6 +6206,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
 
@@ -6214,10 +6223,14 @@  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 = 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-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=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
@@ -6232,6 +6245,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
 
@@ -6243,10 +6260,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-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 = 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-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=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
@@ -6268,14 +6289,22 @@  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 = 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); };)
+  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=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
@@ -6294,6 +6323,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
 
@@ -6307,14 +6340,22 @@  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 = 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); };)
+  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=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
@@ -6333,16 +6374,19 @@  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 = 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); };)
+  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=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
@@ -6375,6 +6419,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
 ])
 
diff --git a/tests/ovn.at b/tests/ovn.at
index d362b4c56..2411c653e 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -20601,12 +20601,12 @@  test_ip_packet_larger() {
         expected=${expected}0000000000000000000000000000
         echo $expected > br_phys_n1.expected
     else
-        src_ip=`ip_to_hex 172.168.0.100`
+        src_ip=`ip_to_hex 10 0 0 1`
         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}00004000fe01c55f
+        icmp_reply=${src_mac}${dst_mac}08004500${reply_pkt_len}00004000fe01686b
         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}
@@ -20698,7 +20698,7 @@  test_ip6_packet_larger() {
 
     local ipv6_src=10000000000000000000000000000003
     local ipv6_dst=20000000000000000000000000000002
-    local ipv6_rt=20000000000000000000000000000001
+    local ipv6_rt=10000000000000000000000000000001
 
     local payload=0000000000000000000000000000000000000000
     local payload=${payload}0000000000000000000000000000000000000000
diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at
index 88c72edcd..2341bb387 100644
--- a/tests/system-ovn-kmod.at
+++ b/tests/system-ovn-kmod.at
@@ -977,7 +977,7 @@  dnl check ovn-nbctl lr-lb-add lr lb1
 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
 
@@ -1032,6 +1032,7 @@  NS_CHECK_EXEC([client], [$PYTHON3 ./client.py])
 
 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