diff mbox series

[ovs-dev,v2,1/2] ovn-northd: Match outport for lflows in ROUTER_IN_LARGER_PKTS.

Message ID 20210805153934.3865265-2-hzhou@ovn.org
State Accepted
Headers show
Series Multiple distributed gateway port support. | 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 fail github build: failed

Commit Message

Han Zhou Aug. 5, 2021, 3:39 p.m. UTC
The commit 1c9e46ab5 removed the outport match from the lflows, which
leads to a problem for gateway routers that have multiple ports
configured with different MTUs. For example, R0 has port P1, P2 and P3.
P2 and P3 both have gateway_mtu configured: P2 mtu = 1400, P3 mtu = 1500.
Below lflows are generated:
  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "P1" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {... icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "P1" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {... icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)

These two lflows have exact same match, but different actions (with
different MTUs). This will result in a random one gets installed by
ovn-controller and the icmp4_error message may contain incorrect mtu.
This patch fixes it by adding the outport back for these flows, so that
mtu that matches the outport setting is used in the generated icmp error
messages.

Another problem of the commit is that the ddlog part used Flow instead
of MeterFlow for the gateway router flows that generates icmp errors,
while the flows for Distributed Gateway Ports use MeterFlow. This patch
also fixes that by combining the DGP and gateway router code using
MeterFlow. The check for DGP and Gateway Router is removed to simplify
the code, because checking the gateway_mtu config should be sufficient,
which also makes it consistent with the flows in the ADMISSION and
IP_INPUT stages where we didn't check DGP and gateway router but only
the gateway_mtu settings.

Fixes: 1c9e46ab5 ("northd: add check_pkt_larger lflows for ingress traffic")
Signed-off-by: Han Zhou <hzhou@ovn.org>
---
 northd/ovn-northd.8.xml |   7 +-
 northd/ovn-northd.c     |  49 +++++-----
 northd/ovn_northd.dl    | 195 +++++-----------------------------------
 tests/ovn-northd.at     |  48 +++++-----
 4 files changed, 74 insertions(+), 225 deletions(-)

Comments

Lorenzo Bianconi Aug. 5, 2021, 5:09 p.m. UTC | #1
> The commit 1c9e46ab5 removed the outport match from the lflows, which
> leads to a problem for gateway routers that have multiple ports
> configured with different MTUs. For example, R0 has port P1, P2 and P3.
> P2 and P3 both have gateway_mtu configured: P2 mtu = 1400, P3 mtu = 1500.
> Below lflows are generated:
>   table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "P1" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {... icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
>   table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "P1" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {... icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
> 
> These two lflows have exact same match, but different actions (with
> different MTUs). This will result in a random one gets installed by
> ovn-controller and the icmp4_error message may contain incorrect mtu.
> This patch fixes it by adding the outport back for these flows, so that
> mtu that matches the outport setting is used in the generated icmp error
> messages.
> 
> Another problem of the commit is that the ddlog part used Flow instead
> of MeterFlow for the gateway router flows that generates icmp errors,
> while the flows for Distributed Gateway Ports use MeterFlow. This patch
> also fixes that by combining the DGP and gateway router code using
> MeterFlow. The check for DGP and Gateway Router is removed to simplify
> the code, because checking the gateway_mtu config should be sufficient,
> which also makes it consistent with the flows in the ADMISSION and
> IP_INPUT stages where we didn't check DGP and gateway router but only
> the gateway_mtu settings.
> 
> Fixes: 1c9e46ab5 ("northd: add check_pkt_larger lflows for ingress traffic")
> Signed-off-by: Han Zhou <hzhou@ovn.org>

Hi Han,

I have not run any test yet, but the code seems fine to me.

Regards,
Lorenzo

> ---
>  northd/ovn-northd.8.xml |   7 +-
>  northd/ovn-northd.c     |  49 +++++-----
>  northd/ovn_northd.dl    | 195 +++++-----------------------------------
>  tests/ovn-northd.at     |  48 +++++-----
>  4 files changed, 74 insertions(+), 225 deletions(-)
> 
> diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
> index 08d484760..de4fe90c7 100644
> --- a/northd/ovn-northd.8.xml
> +++ b/northd/ovn-northd.8.xml
> @@ -3756,9 +3756,10 @@ REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); next;
>        configured with <code>options:gateway_mtu</code> to a valid integer
>        value, this table adds the following priority-150 logical flow for each
>        logical router port with the match <code>inport == <var>LRP</var>
> -      &amp;&amp; REGBIT_PKT_LARGER &amp;&amp; !REGBIT_EGRESS_LOOPBACK</code>,
> -      where <var>LRP</var> is the logical router port and applies the following
> -      action for ipv4 and ipv6 respectively:
> +      &amp;&amp; outport == <var>GW_PORT</var> &amp;&amp; REGBIT_PKT_LARGER
> +      &amp;&amp; !REGBIT_EGRESS_LOOPBACK</code>, where <var>LRP</var> is the
> +      logical router port and <var>GW_PORT</var> is the gateway port and
> +      applies the following action for ipv4 and ipv6 respectively:
>      </p>
>  
>      <pre>
> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index a0eaa1247..605e33486 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -10894,13 +10894,19 @@ build_arp_resolve_flows_for_lrouter_port(
>  static void
>  build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
>                              struct shash *meter_groups, struct ds *match,
> -                            struct ds *actions, enum ovn_stage stage)
> +                            struct ds *actions, enum ovn_stage stage,
> +                            struct ovn_port *outport)
>  {
> +    char *outport_match = NULL;
> +    if (outport) {
> +        outport_match = xasprintf("outport == %s && ", outport->json_key);
> +    }
> +
>      if (op->lrp_networks.ipv4_addrs) {
>          ds_clear(match);
> -        ds_put_format(match,
> -                      "inport == %s && ip4 && "REGBIT_PKT_LARGER
> -                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key);
> +        ds_put_format(match, "inport == %s && %sip4 && "REGBIT_PKT_LARGER
> +                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
> +                      outport ? outport_match : "");
>  
>          ds_clear(actions);
>          /* Set icmp4.frag_mtu to gw_mtu */
> @@ -10931,8 +10937,9 @@ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
>  
>      if (op->lrp_networks.ipv6_addrs) {
>          ds_clear(match);
> -        ds_put_format(match, "inport == %s && ip6 && "REGBIT_PKT_LARGER
> -                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key);
> +        ds_put_format(match, "inport == %s && %sip6 && "REGBIT_PKT_LARGER
> +                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
> +                      outport ? outport_match : "");
>  
>          ds_clear(actions);
>          /* Set icmp6.frag_mtu to gw_mtu */
> @@ -10960,6 +10967,9 @@ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
>                                          meter_groups),
>                                    &op->nbrp->header_);
>      }
> +    if (outport) {
> +        free(outport_match);
> +    }
>  }
>  
>  static int
> @@ -10999,7 +11009,8 @@ build_check_pkt_len_flows_for_lrp(struct ovn_port *op,
>  
>      /* ingress traffic */
>      build_icmperr_pkt_big_flows(op, gw_mtu, lflows, meter_groups,
> -                                match, actions, S_ROUTER_IN_IP_INPUT);
> +                                match, actions, S_ROUTER_IN_IP_INPUT,
> +                                NULL);
>  
>      for (size_t i = 0; i < op->od->nbr->n_ports; i++) {
>          struct ovn_port *rp = ovn_port_find(ports,
> @@ -11010,7 +11021,8 @@ build_check_pkt_len_flows_for_lrp(struct ovn_port *op,
>  
>          /* egress traffic */
>          build_icmperr_pkt_big_flows(rp, gw_mtu, lflows, meter_groups,
> -                                    match, actions, S_ROUTER_IN_LARGER_PKTS);
> +                                    match, actions, S_ROUTER_IN_LARGER_PKTS,
> +                                    op);
>      }
>  }
>  
> @@ -11044,21 +11056,14 @@ build_check_pkt_len_flows_for_lrouter(
>      ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 0, "1",
>                    "next;");
>  
> -    if (od->l3dgw_port && od->l3redirect_port) {
> -        /* gw router port */
> -        build_check_pkt_len_flows_for_lrp(od->l3dgw_port, lflows,
> -                                          ports, meter_groups, match, actions);
> -    } else if (smap_get(&od->nbr->options, "chassis")) {
> -        for (size_t i = 0; i < od->nbr->n_ports; i++) {
> -            /* gw router */
> -            struct ovn_port *rp = ovn_port_find(ports,
> -                                                od->nbr->ports[i]->name);
> -            if (!rp) {
> -                continue;
> -            }
> -            build_check_pkt_len_flows_for_lrp(rp, lflows, ports, meter_groups,
> -                                              match, actions);
> +    for (size_t i = 0; i < od->nbr->n_ports; i++) {
> +        struct ovn_port *rp = ovn_port_find(ports,
> +                                            od->nbr->ports[i]->name);
> +        if (!rp || !rp->nbrp) {
> +            continue;
>          }
> +        build_check_pkt_len_flows_for_lrp(rp, lflows, ports, meter_groups,
> +                                          match, actions);
>      }
>  }
>  
> diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
> index 091fe10b3..d7141294e 100644
> --- a/northd/ovn_northd.dl
> +++ b/northd/ovn_northd.dl
> @@ -7508,29 +7508,16 @@ for (&Router(._uuid = lr_uuid))
>  
>  /* Local router ingress table CHK_PKT_LEN: Check packet length.
>   *
> - * For distributed routers with gateway ports.
> - * Any IPv4 or IPv6 packet with outport set to the distributed gateway
> - * router port, check the packet length and store the result in the
> - * 'REGBIT_PKT_LARGER' register bit.
> + * Any IPv4 or IPv6 packet with outport set to a router port that has
> + * gateway_mtu > 0 configured, check the packet length and store the result in
> + * the 'REGBIT_PKT_LARGER' register bit.
>   *
>   * Local router ingress table LARGER_PKTS: Handle larger packets.
>   *
> - * Any IPv4 or IPv6 packet with outport set to the distributed gateway
> - * router port and the 'REGBIT_PKT_LARGER' register bit is set,
> + * Any IPv4 or IPv6 packet with outport set to a router port that has
> + * gatway_mtu > 0 configured and the 'REGBIT_PKT_LARGER' register bit is set,
>   * generate an ICMPv4/ICMPv6 packet with type 3/2 (Destination
>   * Unreachable/Packet Too Big) and code 4/0 (Fragmentation needed).
> - *
> - * For Gateway routers.
> - * Any IPv4 or IPv6 packet with outport set to the router port which has
> - * the option 'gateway_mtu' set, check the packet length and store
> - * the result in the 'REGBIT_PKT_LARGER' register bit.
> - *
> - * Local router ingress table LARGER_PKTS: Handle larger packets.
> - *
> - * Any IPv4 or IPv6 packet with outport set to the router port which has
> - * the option 'gateway_mtu' set and the 'REGBIT_PKT_LARGER' register bit
> - * is set, generate ICMPv4/ICMPv6 packet with type 3/2 (Destination
> - * Unreachable/Packet Too Big) and * code 4/0 (Fragmentation needed).
>   */
>  Flow(.logical_datapath = lr_uuid,
>       .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
> @@ -7549,21 +7536,19 @@ Flow(.logical_datapath = lr_uuid,
>  Flow(.logical_datapath = lr_uuid,
>       .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
>       .priority         = 50,
> -     .__match          = "outport == ${l3dgw_port_json_name}",
> +     .__match          = "outport == ${gw_mtu_rp.json_name}",
>       .actions          = "${rEGBIT_PKT_LARGER()} = check_pkt_larger(${mtu}); "
>                           "next;",
> -     .external_ids     = stage_hint(l3dgw_port._uuid)) :-
> +     .external_ids     = stage_hint(gw_mtu_rp.lrp._uuid)) :-
>      r in &Router(._uuid = lr_uuid),
> -    Some{var l3dgw_port} = r.l3dgw_port,
> -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> -    r.redirect_port_name != "",
> -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> +    gw_mtu_rp in &RouterPort(.router = r),
> +    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
>      gw_mtu > 0,
>      var mtu = gw_mtu + vLAN_ETH_HEADER_LEN().
>  MeteredFlow(.logical_datapath = lr_uuid,
>              .stage            = s_ROUTER_IN_LARGER_PKTS(),
>              .priority         = 150,
> -            .__match          = "inport == ${rp.json_name} && ip4 && "
> +            .__match          = "inport == ${rp.json_name} && outport == ${gw_mtu_rp.json_name} && ip4 && "
>                                  "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
>              .actions          = "icmp4_error {"
>                                  "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> @@ -7582,13 +7567,12 @@ MeteredFlow(.logical_datapath = lr_uuid,
>              .controller_meter = r.copp.get(cOPP_ICMP4_ERR()),
>              .external_ids     = stage_hint(rp.lrp._uuid)) :-
>      r in &Router(._uuid = lr_uuid),
> -    Some{var l3dgw_port} = r.l3dgw_port,
> -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> -    r.redirect_port_name != "",
> -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> +    gw_mtu_rp in &RouterPort(.router = r),
> +    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
>      gw_mtu > 0,
> +    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
>      rp in &RouterPort(.router = r),
> -    rp.lrp != l3dgw_port,
> +    rp.lrp != gw_mtu_rp.lrp,
>      Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
>  
>  MeteredFlow(.logical_datapath = lr_uuid,
> @@ -7613,19 +7597,18 @@ MeteredFlow(.logical_datapath = lr_uuid,
>              .controller_meter = r.copp.get(cOPP_ICMP4_ERR()),
>              .external_ids     = stage_hint(rp.lrp._uuid)) :-
>      r in &Router(._uuid = lr_uuid),
> -    Some{var l3dgw_port} = r.l3dgw_port,
> -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> -    r.redirect_port_name != "",
> -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> +    gw_mtu_rp in &RouterPort(.router = r),
> +    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
>      gw_mtu > 0,
> +    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
>      rp in &RouterPort(.router = r),
> -    rp.lrp == l3dgw_port,
> +    rp.lrp == gw_mtu_rp.lrp,
>      Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
>  
>  MeteredFlow(.logical_datapath = lr_uuid,
>              .stage            = s_ROUTER_IN_LARGER_PKTS(),
>              .priority         = 150,
> -            .__match          = "inport == ${rp.json_name} && ip6 && "
> +            .__match          = "inport == ${rp.json_name} && outport == ${gw_mtu_rp.json_name} && ip6 && "
>                                  "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
>              .actions          = "icmp6_error {"
>                                  "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> @@ -7644,13 +7627,12 @@ MeteredFlow(.logical_datapath = lr_uuid,
>              .controller_meter = r.copp.get(cOPP_ICMP6_ERR()),
>              .external_ids     = stage_hint(rp.lrp._uuid)) :-
>      r in &Router(._uuid = lr_uuid),
> -    Some{var l3dgw_port} = r.l3dgw_port,
> -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> -    r.redirect_port_name != "",
> -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> +    gw_mtu_rp in &RouterPort(.router = r),
> +    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
>      gw_mtu > 0,
> +    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
>      rp in &RouterPort(.router = r),
> -    rp.lrp != l3dgw_port,
> +    rp.lrp != gw_mtu_rp.lrp,
>      Some{var first_ipv6} = rp.networks.ipv6_addrs.nth(0).
>  
>  MeteredFlow(.logical_datapath = lr_uuid,
> @@ -7675,137 +7657,6 @@ MeteredFlow(.logical_datapath = lr_uuid,
>              .controller_meter = r.copp.get(cOPP_ICMP6_ERR()),
>              .external_ids     = stage_hint(rp.lrp._uuid)) :-
>      r in &Router(._uuid = lr_uuid),
> -    Some{var l3dgw_port} = r.l3dgw_port,
> -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> -    r.redirect_port_name != "",
> -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> -    gw_mtu > 0,
> -    rp in &RouterPort(.router = r),
> -    rp.lrp == l3dgw_port,
> -    Some{var first_ipv6} = rp.networks.ipv6_addrs.nth(0).
> -
> -/* Gateway routers. */
> -Flow(.logical_datapath = lr_uuid,
> -     .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
> -     .priority         = 50,
> -     .__match          = "outport == ${gw_mtu_rp.json_name}",
> -     .actions          = "${rEGBIT_PKT_LARGER()} = check_pkt_larger(${mtu}); "
> -                         "next;",
> -     .external_ids     = stage_hint(gw_mtu_rp.lrp._uuid)) :-
> -    r in &Router(._uuid = lr_uuid),
> -    r.is_gateway,
> -    gw_mtu_rp in &RouterPort(.router = r),
> -    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> -    gw_mtu > 0,
> -    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN().
> -Flow(.logical_datapath = lr_uuid,
> -     .stage            = s_ROUTER_IN_LARGER_PKTS(),
> -     .priority         = 150,
> -     .__match          = "inport == ${rp.json_name} && ip4 && "
> -                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> -     .actions          = "icmp4_error {"
> -                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> -                         "${rEGBIT_PKT_LARGER()} = 0; "
> -                         "eth.dst = ${rp.networks.ea}; "
> -                         "ip4.dst = ip4.src; "
> -                         "ip4.src = ${first_ipv4.addr}; "
> -                         "ip.ttl = 255; "
> -                         "icmp4.type = 3; /* Destination Unreachable. */ "
> -                         "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
> -                         /* Set icmp4.frag_mtu to gw_mtu */
> -                         "icmp4.frag_mtu = ${gw_mtu}; "
> -                         "next(pipeline=ingress, table=0); "
> -                         "};",
> -     .external_ids     = stage_hint(rp.lrp._uuid)) :-
> -    r in &Router(._uuid = lr_uuid),
> -    r.is_gateway,
> -    gw_mtu_rp in &RouterPort(.router = r),
> -    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> -    gw_mtu > 0,
> -    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
> -    rp in &RouterPort(.router = r),
> -    rp.lrp != gw_mtu_rp.lrp,
> -    Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
> -
> -Flow(.logical_datapath = lr_uuid,
> -     .stage            = s_ROUTER_IN_IP_INPUT(),
> -     .priority         = 150,
> -     .__match          = "inport == ${rp.json_name} && ip4 && "
> -                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> -     .actions          = "icmp4_error {"
> -                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> -                         "${rEGBIT_PKT_LARGER()} = 0; "
> -                         "eth.dst = ${rp.networks.ea}; "
> -                         "ip4.dst = ip4.src; "
> -                         "ip4.src = ${first_ipv4.addr}; "
> -                         "ip.ttl = 255; "
> -                         "icmp4.type = 3; /* Destination Unreachable. */ "
> -                         "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
> -                         /* Set icmp4.frag_mtu to gw_mtu */
> -                         "icmp4.frag_mtu = ${gw_mtu}; "
> -                         "next(pipeline=ingress, table=0); "
> -                         "};",
> -     .external_ids     = stage_hint(rp.lrp._uuid)) :-
> -    r in &Router(._uuid = lr_uuid),
> -    r.is_gateway,
> -    gw_mtu_rp in &RouterPort(.router = r),
> -    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> -    gw_mtu > 0,
> -    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
> -    rp in &RouterPort(.router = r),
> -    rp.lrp == gw_mtu_rp.lrp,
> -    Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
> -
> -Flow(.logical_datapath = lr_uuid,
> -     .stage            = s_ROUTER_IN_LARGER_PKTS(),
> -     .priority         = 150,
> -     .__match          = "inport == ${rp.json_name} && ip6 && "
> -                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> -     .actions          = "icmp6_error {"
> -                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> -                         "${rEGBIT_PKT_LARGER()} = 0; "
> -                         "eth.dst = ${rp.networks.ea}; "
> -                         "ip6.dst = ip6.src; "
> -                         "ip6.src = ${first_ipv6.addr}; "
> -                         "ip.ttl = 255; "
> -                         "icmp6.type = 2; /* Packet Too Big. */ "
> -                         "icmp6.code = 0; "
> -                         /* Set icmp6.frag_mtu to gw_mtu */
> -                         "icmp6.frag_mtu = ${gw_mtu}; "
> -                         "next(pipeline=ingress, table=0); "
> -                         "};",
> -     .external_ids     = stage_hint(rp.lrp._uuid)) :-
> -    r in &Router(._uuid = lr_uuid),
> -    r.is_gateway,
> -    gw_mtu_rp in &RouterPort(.router = r),
> -    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> -    gw_mtu > 0,
> -    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
> -    rp in &RouterPort(.router = r),
> -    rp.lrp != gw_mtu_rp.lrp,
> -    Some{var first_ipv6} = rp.networks.ipv6_addrs.nth(0).
> -
> -Flow(.logical_datapath = lr_uuid,
> -     .stage            = s_ROUTER_IN_IP_INPUT(),
> -     .priority         = 150,
> -     .__match          = "inport == ${rp.json_name} && ip6 && "
> -                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> -     .actions          = "icmp6_error {"
> -                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> -                         "${rEGBIT_PKT_LARGER()} = 0; "
> -                         "eth.dst = ${rp.networks.ea}; "
> -                         "ip6.dst = ip6.src; "
> -                         "ip6.src = ${first_ipv6.addr}; "
> -                         "ip.ttl = 255; "
> -                         "icmp6.type = 2; /* Packet Too Big. */ "
> -                         "icmp6.code = 0; "
> -                         /* Set icmp6.frag_mtu to gw_mtu */
> -                         "icmp6.frag_mtu = ${gw_mtu}; "
> -                         "next(pipeline=ingress, table=0); "
> -                         "};",
> -     .external_ids     = stage_hint(rp.lrp._uuid)) :-
> -    r in &Router(._uuid = lr_uuid),
> -    r.is_gateway,
>      gw_mtu_rp in &RouterPort(.router = r),
>      var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
>      gw_mtu > 0,
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 474e88021..2098b1c19 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -4865,10 +4865,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
>    table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
>    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
>    table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && 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=16(lr_in_larger_pkts  ), 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 = 1500; next(pipeline=ingress, table=0); };)
> -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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); };)
>  ])
>  
>  AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
> @@ -4886,14 +4886,6 @@ AT_CHECK([grep -E "lr_in_ip_input.*icmp6_error" lr0flows | sort], [0], [dnl
>  # Clear the gateway-chassis for lr0-public
>  check ovn-nbctl --wait=sb clear logical_router_port lr0-public gateway_chassis
>  
> -ovn-sbctl dump-flows lr0 > lr0flows
> -AT_CAPTURE_FILE([sw0flows])
> -
> -AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [dnl
> -  table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
> -  table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> -])
> -
>  # Make lr0 as a gateway router.
>  check ovn-nbctl --wait=sb set logical_router lr0 options:chassis=ch1
>  
> @@ -4904,10 +4896,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
>    table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
>    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
>    table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && 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=16(lr_in_larger_pkts  ), 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 = 1500; next(pipeline=ingress, table=0); };)
> -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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); };)
>  ])
>  
>  AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
> @@ -4933,14 +4925,14 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
>    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
>    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); next;)
>    table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && 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=16(lr_in_larger_pkts  ), 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 = 1400; next(pipeline=ingress, table=0); };)
> -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && 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=16(lr_in_larger_pkts  ), 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 = 1500; next(pipeline=ingress, table=0); };)
> -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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=16(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=16(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=16(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=16(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); };)
>  ])
>  
>  AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
> @@ -4968,10 +4960,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
>    table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
>    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); next;)
>    table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && 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=16(lr_in_larger_pkts  ), 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 = 1400; next(pipeline=ingress, table=0); };)
> -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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); };)
>  ])
>  
>  AT_CLEANUP
> -- 
> 2.30.2
> 
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Numan Siddique Aug. 6, 2021, 6:59 p.m. UTC | #2
On Thu, Aug 5, 2021 at 1:10 PM Lorenzo Bianconi
<lorenzo.bianconi@redhat.com> wrote:
>
> > The commit 1c9e46ab5 removed the outport match from the lflows, which
> > leads to a problem for gateway routers that have multiple ports
> > configured with different MTUs. For example, R0 has port P1, P2 and P3.
> > P2 and P3 both have gateway_mtu configured: P2 mtu = 1400, P3 mtu = 1500.
> > Below lflows are generated:
> >   table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "P1" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {... icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
> >   table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "P1" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {... icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
> >
> > These two lflows have exact same match, but different actions (with
> > different MTUs). This will result in a random one gets installed by
> > ovn-controller and the icmp4_error message may contain incorrect mtu.
> > This patch fixes it by adding the outport back for these flows, so that
> > mtu that matches the outport setting is used in the generated icmp error
> > messages.
> >
> > Another problem of the commit is that the ddlog part used Flow instead
> > of MeterFlow for the gateway router flows that generates icmp errors,
> > while the flows for Distributed Gateway Ports use MeterFlow. This patch
> > also fixes that by combining the DGP and gateway router code using
> > MeterFlow. The check for DGP and Gateway Router is removed to simplify
> > the code, because checking the gateway_mtu config should be sufficient,
> > which also makes it consistent with the flows in the ADMISSION and
> > IP_INPUT stages where we didn't check DGP and gateway router but only
> > the gateway_mtu settings.
> >
> > Fixes: 1c9e46ab5 ("northd: add check_pkt_larger lflows for ingress traffic")
> > Signed-off-by: Han Zhou <hzhou@ovn.org>
>
> Hi Han,
>
> I have not run any test yet, but the code seems fine to me.
>
> Regards,
> Lorenzo

Thanks for fixing this.

Please see below  one small nit comment.

With that addressed:

Acked-by: Numan Siddique <numans@ovn.org>

Numan

>
> > ---
> >  northd/ovn-northd.8.xml |   7 +-
> >  northd/ovn-northd.c     |  49 +++++-----
> >  northd/ovn_northd.dl    | 195 +++++-----------------------------------
> >  tests/ovn-northd.at     |  48 +++++-----
> >  4 files changed, 74 insertions(+), 225 deletions(-)
> >
> > diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
> > index 08d484760..de4fe90c7 100644
> > --- a/northd/ovn-northd.8.xml
> > +++ b/northd/ovn-northd.8.xml
> > @@ -3756,9 +3756,10 @@ REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); next;
> >        configured with <code>options:gateway_mtu</code> to a valid integer
> >        value, this table adds the following priority-150 logical flow for each
> >        logical router port with the match <code>inport == <var>LRP</var>
> > -      &amp;&amp; REGBIT_PKT_LARGER &amp;&amp; !REGBIT_EGRESS_LOOPBACK</code>,
> > -      where <var>LRP</var> is the logical router port and applies the following
> > -      action for ipv4 and ipv6 respectively:
> > +      &amp;&amp; outport == <var>GW_PORT</var> &amp;&amp; REGBIT_PKT_LARGER
> > +      &amp;&amp; !REGBIT_EGRESS_LOOPBACK</code>, where <var>LRP</var> is the
> > +      logical router port and <var>GW_PORT</var> is the gateway port and
> > +      applies the following action for ipv4 and ipv6 respectively:
> >      </p>
> >
> >      <pre>
> > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > index a0eaa1247..605e33486 100644
> > --- a/northd/ovn-northd.c
> > +++ b/northd/ovn-northd.c
> > @@ -10894,13 +10894,19 @@ build_arp_resolve_flows_for_lrouter_port(
> >  static void
> >  build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
> >                              struct shash *meter_groups, struct ds *match,
> > -                            struct ds *actions, enum ovn_stage stage)
> > +                            struct ds *actions, enum ovn_stage stage,
> > +                            struct ovn_port *outport)
> >  {
> > +    char *outport_match = NULL;
> > +    if (outport) {
> > +        outport_match = xasprintf("outport == %s && ", outport->json_key);
> > +    }
> > +

The above can be simplified as:

char *outport_match = outport ? xasprintf("outport == %s && ",
outport->json_key) : NULL;

> >      if (op->lrp_networks.ipv4_addrs) {
> >          ds_clear(match);
> > -        ds_put_format(match,
> > -                      "inport == %s && ip4 && "REGBIT_PKT_LARGER
> > -                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key);
> > +        ds_put_format(match, "inport == %s && %sip4 && "REGBIT_PKT_LARGER
> > +                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
> > +                      outport ? outport_match : "");
> >
> >          ds_clear(actions);
> >          /* Set icmp4.frag_mtu to gw_mtu */
> > @@ -10931,8 +10937,9 @@ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
> >
> >      if (op->lrp_networks.ipv6_addrs) {
> >          ds_clear(match);
> > -        ds_put_format(match, "inport == %s && ip6 && "REGBIT_PKT_LARGER
> > -                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key);
> > +        ds_put_format(match, "inport == %s && %sip6 && "REGBIT_PKT_LARGER
> > +                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
> > +                      outport ? outport_match : "");
> >
> >          ds_clear(actions);
> >          /* Set icmp6.frag_mtu to gw_mtu */
> > @@ -10960,6 +10967,9 @@ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
> >                                          meter_groups),
> >                                    &op->nbrp->header_);
> >      }
> > +    if (outport) {
> > +        free(outport_match);
> > +    }

There is no need to check for outport to not NULL before freeing.

Thanks
Numan

> >  }
> >
> >  static int
> > @@ -10999,7 +11009,8 @@ build_check_pkt_len_flows_for_lrp(struct ovn_port *op,
> >
> >      /* ingress traffic */
> >      build_icmperr_pkt_big_flows(op, gw_mtu, lflows, meter_groups,
> > -                                match, actions, S_ROUTER_IN_IP_INPUT);
> > +                                match, actions, S_ROUTER_IN_IP_INPUT,
> > +                                NULL);
> >
> >      for (size_t i = 0; i < op->od->nbr->n_ports; i++) {
> >          struct ovn_port *rp = ovn_port_find(ports,
> > @@ -11010,7 +11021,8 @@ build_check_pkt_len_flows_for_lrp(struct ovn_port *op,
> >
> >          /* egress traffic */
> >          build_icmperr_pkt_big_flows(rp, gw_mtu, lflows, meter_groups,
> > -                                    match, actions, S_ROUTER_IN_LARGER_PKTS);
> > +                                    match, actions, S_ROUTER_IN_LARGER_PKTS,
> > +                                    op);
> >      }
> >  }
> >
> > @@ -11044,21 +11056,14 @@ build_check_pkt_len_flows_for_lrouter(
> >      ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 0, "1",
> >                    "next;");
> >
> > -    if (od->l3dgw_port && od->l3redirect_port) {
> > -        /* gw router port */
> > -        build_check_pkt_len_flows_for_lrp(od->l3dgw_port, lflows,
> > -                                          ports, meter_groups, match, actions);
> > -    } else if (smap_get(&od->nbr->options, "chassis")) {
> > -        for (size_t i = 0; i < od->nbr->n_ports; i++) {
> > -            /* gw router */
> > -            struct ovn_port *rp = ovn_port_find(ports,
> > -                                                od->nbr->ports[i]->name);
> > -            if (!rp) {
> > -                continue;
> > -            }
> > -            build_check_pkt_len_flows_for_lrp(rp, lflows, ports, meter_groups,
> > -                                              match, actions);
> > +    for (size_t i = 0; i < od->nbr->n_ports; i++) {
> > +        struct ovn_port *rp = ovn_port_find(ports,
> > +                                            od->nbr->ports[i]->name);
> > +        if (!rp || !rp->nbrp) {
> > +            continue;
> >          }
> > +        build_check_pkt_len_flows_for_lrp(rp, lflows, ports, meter_groups,
> > +                                          match, actions);
> >      }
> >  }
> >
> > diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
> > index 091fe10b3..d7141294e 100644
> > --- a/northd/ovn_northd.dl
> > +++ b/northd/ovn_northd.dl
> > @@ -7508,29 +7508,16 @@ for (&Router(._uuid = lr_uuid))
> >
> >  /* Local router ingress table CHK_PKT_LEN: Check packet length.
> >   *
> > - * For distributed routers with gateway ports.
> > - * Any IPv4 or IPv6 packet with outport set to the distributed gateway
> > - * router port, check the packet length and store the result in the
> > - * 'REGBIT_PKT_LARGER' register bit.
> > + * Any IPv4 or IPv6 packet with outport set to a router port that has
> > + * gateway_mtu > 0 configured, check the packet length and store the result in
> > + * the 'REGBIT_PKT_LARGER' register bit.
> >   *
> >   * Local router ingress table LARGER_PKTS: Handle larger packets.
> >   *
> > - * Any IPv4 or IPv6 packet with outport set to the distributed gateway
> > - * router port and the 'REGBIT_PKT_LARGER' register bit is set,
> > + * Any IPv4 or IPv6 packet with outport set to a router port that has
> > + * gatway_mtu > 0 configured and the 'REGBIT_PKT_LARGER' register bit is set,
> >   * generate an ICMPv4/ICMPv6 packet with type 3/2 (Destination
> >   * Unreachable/Packet Too Big) and code 4/0 (Fragmentation needed).
> > - *
> > - * For Gateway routers.
> > - * Any IPv4 or IPv6 packet with outport set to the router port which has
> > - * the option 'gateway_mtu' set, check the packet length and store
> > - * the result in the 'REGBIT_PKT_LARGER' register bit.
> > - *
> > - * Local router ingress table LARGER_PKTS: Handle larger packets.
> > - *
> > - * Any IPv4 or IPv6 packet with outport set to the router port which has
> > - * the option 'gateway_mtu' set and the 'REGBIT_PKT_LARGER' register bit
> > - * is set, generate ICMPv4/ICMPv6 packet with type 3/2 (Destination
> > - * Unreachable/Packet Too Big) and * code 4/0 (Fragmentation needed).
> >   */
> >  Flow(.logical_datapath = lr_uuid,
> >       .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
> > @@ -7549,21 +7536,19 @@ Flow(.logical_datapath = lr_uuid,
> >  Flow(.logical_datapath = lr_uuid,
> >       .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
> >       .priority         = 50,
> > -     .__match          = "outport == ${l3dgw_port_json_name}",
> > +     .__match          = "outport == ${gw_mtu_rp.json_name}",
> >       .actions          = "${rEGBIT_PKT_LARGER()} = check_pkt_larger(${mtu}); "
> >                           "next;",
> > -     .external_ids     = stage_hint(l3dgw_port._uuid)) :-
> > +     .external_ids     = stage_hint(gw_mtu_rp.lrp._uuid)) :-
> >      r in &Router(._uuid = lr_uuid),
> > -    Some{var l3dgw_port} = r.l3dgw_port,
> > -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> > -    r.redirect_port_name != "",
> > -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> > +    gw_mtu_rp in &RouterPort(.router = r),
> > +    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> >      gw_mtu > 0,
> >      var mtu = gw_mtu + vLAN_ETH_HEADER_LEN().
> >  MeteredFlow(.logical_datapath = lr_uuid,
> >              .stage            = s_ROUTER_IN_LARGER_PKTS(),
> >              .priority         = 150,
> > -            .__match          = "inport == ${rp.json_name} && ip4 && "
> > +            .__match          = "inport == ${rp.json_name} && outport == ${gw_mtu_rp.json_name} && ip4 && "
> >                                  "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> >              .actions          = "icmp4_error {"
> >                                  "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> > @@ -7582,13 +7567,12 @@ MeteredFlow(.logical_datapath = lr_uuid,
> >              .controller_meter = r.copp.get(cOPP_ICMP4_ERR()),
> >              .external_ids     = stage_hint(rp.lrp._uuid)) :-
> >      r in &Router(._uuid = lr_uuid),
> > -    Some{var l3dgw_port} = r.l3dgw_port,
> > -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> > -    r.redirect_port_name != "",
> > -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> > +    gw_mtu_rp in &RouterPort(.router = r),
> > +    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> >      gw_mtu > 0,
> > +    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
> >      rp in &RouterPort(.router = r),
> > -    rp.lrp != l3dgw_port,
> > +    rp.lrp != gw_mtu_rp.lrp,
> >      Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
> >
> >  MeteredFlow(.logical_datapath = lr_uuid,
> > @@ -7613,19 +7597,18 @@ MeteredFlow(.logical_datapath = lr_uuid,
> >              .controller_meter = r.copp.get(cOPP_ICMP4_ERR()),
> >              .external_ids     = stage_hint(rp.lrp._uuid)) :-
> >      r in &Router(._uuid = lr_uuid),
> > -    Some{var l3dgw_port} = r.l3dgw_port,
> > -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> > -    r.redirect_port_name != "",
> > -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> > +    gw_mtu_rp in &RouterPort(.router = r),
> > +    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> >      gw_mtu > 0,
> > +    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
> >      rp in &RouterPort(.router = r),
> > -    rp.lrp == l3dgw_port,
> > +    rp.lrp == gw_mtu_rp.lrp,
> >      Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
> >
> >  MeteredFlow(.logical_datapath = lr_uuid,
> >              .stage            = s_ROUTER_IN_LARGER_PKTS(),
> >              .priority         = 150,
> > -            .__match          = "inport == ${rp.json_name} && ip6 && "
> > +            .__match          = "inport == ${rp.json_name} && outport == ${gw_mtu_rp.json_name} && ip6 && "
> >                                  "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> >              .actions          = "icmp6_error {"
> >                                  "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> > @@ -7644,13 +7627,12 @@ MeteredFlow(.logical_datapath = lr_uuid,
> >              .controller_meter = r.copp.get(cOPP_ICMP6_ERR()),
> >              .external_ids     = stage_hint(rp.lrp._uuid)) :-
> >      r in &Router(._uuid = lr_uuid),
> > -    Some{var l3dgw_port} = r.l3dgw_port,
> > -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> > -    r.redirect_port_name != "",
> > -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> > +    gw_mtu_rp in &RouterPort(.router = r),
> > +    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> >      gw_mtu > 0,
> > +    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
> >      rp in &RouterPort(.router = r),
> > -    rp.lrp != l3dgw_port,
> > +    rp.lrp != gw_mtu_rp.lrp,
> >      Some{var first_ipv6} = rp.networks.ipv6_addrs.nth(0).
> >
> >  MeteredFlow(.logical_datapath = lr_uuid,
> > @@ -7675,137 +7657,6 @@ MeteredFlow(.logical_datapath = lr_uuid,
> >              .controller_meter = r.copp.get(cOPP_ICMP6_ERR()),
> >              .external_ids     = stage_hint(rp.lrp._uuid)) :-
> >      r in &Router(._uuid = lr_uuid),
> > -    Some{var l3dgw_port} = r.l3dgw_port,
> > -    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
> > -    r.redirect_port_name != "",
> > -    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
> > -    gw_mtu > 0,
> > -    rp in &RouterPort(.router = r),
> > -    rp.lrp == l3dgw_port,
> > -    Some{var first_ipv6} = rp.networks.ipv6_addrs.nth(0).
> > -
> > -/* Gateway routers. */
> > -Flow(.logical_datapath = lr_uuid,
> > -     .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
> > -     .priority         = 50,
> > -     .__match          = "outport == ${gw_mtu_rp.json_name}",
> > -     .actions          = "${rEGBIT_PKT_LARGER()} = check_pkt_larger(${mtu}); "
> > -                         "next;",
> > -     .external_ids     = stage_hint(gw_mtu_rp.lrp._uuid)) :-
> > -    r in &Router(._uuid = lr_uuid),
> > -    r.is_gateway,
> > -    gw_mtu_rp in &RouterPort(.router = r),
> > -    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> > -    gw_mtu > 0,
> > -    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN().
> > -Flow(.logical_datapath = lr_uuid,
> > -     .stage            = s_ROUTER_IN_LARGER_PKTS(),
> > -     .priority         = 150,
> > -     .__match          = "inport == ${rp.json_name} && ip4 && "
> > -                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> > -     .actions          = "icmp4_error {"
> > -                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> > -                         "${rEGBIT_PKT_LARGER()} = 0; "
> > -                         "eth.dst = ${rp.networks.ea}; "
> > -                         "ip4.dst = ip4.src; "
> > -                         "ip4.src = ${first_ipv4.addr}; "
> > -                         "ip.ttl = 255; "
> > -                         "icmp4.type = 3; /* Destination Unreachable. */ "
> > -                         "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
> > -                         /* Set icmp4.frag_mtu to gw_mtu */
> > -                         "icmp4.frag_mtu = ${gw_mtu}; "
> > -                         "next(pipeline=ingress, table=0); "
> > -                         "};",
> > -     .external_ids     = stage_hint(rp.lrp._uuid)) :-
> > -    r in &Router(._uuid = lr_uuid),
> > -    r.is_gateway,
> > -    gw_mtu_rp in &RouterPort(.router = r),
> > -    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> > -    gw_mtu > 0,
> > -    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
> > -    rp in &RouterPort(.router = r),
> > -    rp.lrp != gw_mtu_rp.lrp,
> > -    Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
> > -
> > -Flow(.logical_datapath = lr_uuid,
> > -     .stage            = s_ROUTER_IN_IP_INPUT(),
> > -     .priority         = 150,
> > -     .__match          = "inport == ${rp.json_name} && ip4 && "
> > -                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> > -     .actions          = "icmp4_error {"
> > -                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> > -                         "${rEGBIT_PKT_LARGER()} = 0; "
> > -                         "eth.dst = ${rp.networks.ea}; "
> > -                         "ip4.dst = ip4.src; "
> > -                         "ip4.src = ${first_ipv4.addr}; "
> > -                         "ip.ttl = 255; "
> > -                         "icmp4.type = 3; /* Destination Unreachable. */ "
> > -                         "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
> > -                         /* Set icmp4.frag_mtu to gw_mtu */
> > -                         "icmp4.frag_mtu = ${gw_mtu}; "
> > -                         "next(pipeline=ingress, table=0); "
> > -                         "};",
> > -     .external_ids     = stage_hint(rp.lrp._uuid)) :-
> > -    r in &Router(._uuid = lr_uuid),
> > -    r.is_gateway,
> > -    gw_mtu_rp in &RouterPort(.router = r),
> > -    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> > -    gw_mtu > 0,
> > -    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
> > -    rp in &RouterPort(.router = r),
> > -    rp.lrp == gw_mtu_rp.lrp,
> > -    Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
> > -
> > -Flow(.logical_datapath = lr_uuid,
> > -     .stage            = s_ROUTER_IN_LARGER_PKTS(),
> > -     .priority         = 150,
> > -     .__match          = "inport == ${rp.json_name} && ip6 && "
> > -                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> > -     .actions          = "icmp6_error {"
> > -                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> > -                         "${rEGBIT_PKT_LARGER()} = 0; "
> > -                         "eth.dst = ${rp.networks.ea}; "
> > -                         "ip6.dst = ip6.src; "
> > -                         "ip6.src = ${first_ipv6.addr}; "
> > -                         "ip.ttl = 255; "
> > -                         "icmp6.type = 2; /* Packet Too Big. */ "
> > -                         "icmp6.code = 0; "
> > -                         /* Set icmp6.frag_mtu to gw_mtu */
> > -                         "icmp6.frag_mtu = ${gw_mtu}; "
> > -                         "next(pipeline=ingress, table=0); "
> > -                         "};",
> > -     .external_ids     = stage_hint(rp.lrp._uuid)) :-
> > -    r in &Router(._uuid = lr_uuid),
> > -    r.is_gateway,
> > -    gw_mtu_rp in &RouterPort(.router = r),
> > -    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> > -    gw_mtu > 0,
> > -    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
> > -    rp in &RouterPort(.router = r),
> > -    rp.lrp != gw_mtu_rp.lrp,
> > -    Some{var first_ipv6} = rp.networks.ipv6_addrs.nth(0).
> > -
> > -Flow(.logical_datapath = lr_uuid,
> > -     .stage            = s_ROUTER_IN_IP_INPUT(),
> > -     .priority         = 150,
> > -     .__match          = "inport == ${rp.json_name} && ip6 && "
> > -                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
> > -     .actions          = "icmp6_error {"
> > -                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
> > -                         "${rEGBIT_PKT_LARGER()} = 0; "
> > -                         "eth.dst = ${rp.networks.ea}; "
> > -                         "ip6.dst = ip6.src; "
> > -                         "ip6.src = ${first_ipv6.addr}; "
> > -                         "ip.ttl = 255; "
> > -                         "icmp6.type = 2; /* Packet Too Big. */ "
> > -                         "icmp6.code = 0; "
> > -                         /* Set icmp6.frag_mtu to gw_mtu */
> > -                         "icmp6.frag_mtu = ${gw_mtu}; "
> > -                         "next(pipeline=ingress, table=0); "
> > -                         "};",
> > -     .external_ids     = stage_hint(rp.lrp._uuid)) :-
> > -    r in &Router(._uuid = lr_uuid),
> > -    r.is_gateway,
> >      gw_mtu_rp in &RouterPort(.router = r),
> >      var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
> >      gw_mtu > 0,
> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > index 474e88021..2098b1c19 100644
> > --- a/tests/ovn-northd.at
> > +++ b/tests/ovn-northd.at
> > @@ -4865,10 +4865,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
> >    table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
> >    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
> >    table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> > -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && 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=16(lr_in_larger_pkts  ), 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 = 1500; next(pipeline=ingress, table=0); };)
> > -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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); };)
> >  ])
> >
> >  AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
> > @@ -4886,14 +4886,6 @@ AT_CHECK([grep -E "lr_in_ip_input.*icmp6_error" lr0flows | sort], [0], [dnl
> >  # Clear the gateway-chassis for lr0-public
> >  check ovn-nbctl --wait=sb clear logical_router_port lr0-public gateway_chassis
> >
> > -ovn-sbctl dump-flows lr0 > lr0flows
> > -AT_CAPTURE_FILE([sw0flows])
> > -
> > -AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [dnl
> > -  table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
> > -  table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> > -])
> > -
> >  # Make lr0 as a gateway router.
> >  check ovn-nbctl --wait=sb set logical_router lr0 options:chassis=ch1
> >
> > @@ -4904,10 +4896,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
> >    table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
> >    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
> >    table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> > -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && 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=16(lr_in_larger_pkts  ), 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 = 1500; next(pipeline=ingress, table=0); };)
> > -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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); };)
> >  ])
> >
> >  AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
> > @@ -4933,14 +4925,14 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
> >    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
> >    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); next;)
> >    table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> > -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && 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=16(lr_in_larger_pkts  ), 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 = 1400; next(pipeline=ingress, table=0); };)
> > -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && 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=16(lr_in_larger_pkts  ), 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 = 1500; next(pipeline=ingress, table=0); };)
> > -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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=16(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=16(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=16(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=16(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); };)
> >  ])
> >
> >  AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
> > @@ -4968,10 +4960,10 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
> >    table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
> >    table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); next;)
> >    table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
> > -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && 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=16(lr_in_larger_pkts  ), 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 = 1400; next(pipeline=ingress, table=0); };)
> > -  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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); };)
> >  ])
> >
> >  AT_CLEANUP
> > --
> > 2.30.2
> >
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Han Zhou Aug. 11, 2021, 10:54 p.m. UTC | #3
On Fri, Aug 6, 2021 at 12:00 PM Numan Siddique <numans@ovn.org> wrote:
>
> On Thu, Aug 5, 2021 at 1:10 PM Lorenzo Bianconi
> <lorenzo.bianconi@redhat.com> wrote:
> >
> > > The commit 1c9e46ab5 removed the outport match from the lflows, which
> > > leads to a problem for gateway routers that have multiple ports
> > > configured with different MTUs. For example, R0 has port P1, P2 and
P3.
> > > P2 and P3 both have gateway_mtu configured: P2 mtu = 1400, P3 mtu =
1500.
> > > Below lflows are generated:
> > >   table=16(lr_in_larger_pkts  ), priority=150  , match=(inport ==
"P1" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {...
icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
> > >   table=16(lr_in_larger_pkts  ), priority=150  , match=(inport ==
"P1" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {...
icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
> > >
> > > These two lflows have exact same match, but different actions (with
> > > different MTUs). This will result in a random one gets installed by
> > > ovn-controller and the icmp4_error message may contain incorrect mtu.
> > > This patch fixes it by adding the outport back for these flows, so
that
> > > mtu that matches the outport setting is used in the generated icmp
error
> > > messages.
> > >
> > > Another problem of the commit is that the ddlog part used Flow instead
> > > of MeterFlow for the gateway router flows that generates icmp errors,
> > > while the flows for Distributed Gateway Ports use MeterFlow. This
patch
> > > also fixes that by combining the DGP and gateway router code using
> > > MeterFlow. The check for DGP and Gateway Router is removed to simplify
> > > the code, because checking the gateway_mtu config should be
sufficient,
> > > which also makes it consistent with the flows in the ADMISSION and
> > > IP_INPUT stages where we didn't check DGP and gateway router but only
> > > the gateway_mtu settings.
> > >
> > > Fixes: 1c9e46ab5 ("northd: add check_pkt_larger lflows for ingress
traffic")
> > > Signed-off-by: Han Zhou <hzhou@ovn.org>
> >
> > Hi Han,
> >
> > I have not run any test yet, but the code seems fine to me.
> >
> > Regards,
> > Lorenzo
>
> Thanks for fixing this.
>
> Please see below  one small nit comment.
>
> With that addressed:
>
> Acked-by: Numan Siddique <numans@ovn.org>
>
> Numan
>
Thanks Numan. I applied the patch with the nit addressed.
diff mbox series

Patch

diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index 08d484760..de4fe90c7 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -3756,9 +3756,10 @@  REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); next;
       configured with <code>options:gateway_mtu</code> to a valid integer
       value, this table adds the following priority-150 logical flow for each
       logical router port with the match <code>inport == <var>LRP</var>
-      &amp;&amp; REGBIT_PKT_LARGER &amp;&amp; !REGBIT_EGRESS_LOOPBACK</code>,
-      where <var>LRP</var> is the logical router port and applies the following
-      action for ipv4 and ipv6 respectively:
+      &amp;&amp; outport == <var>GW_PORT</var> &amp;&amp; REGBIT_PKT_LARGER
+      &amp;&amp; !REGBIT_EGRESS_LOOPBACK</code>, where <var>LRP</var> is the
+      logical router port and <var>GW_PORT</var> is the gateway port and
+      applies the following action for ipv4 and ipv6 respectively:
     </p>
 
     <pre>
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index a0eaa1247..605e33486 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -10894,13 +10894,19 @@  build_arp_resolve_flows_for_lrouter_port(
 static void
 build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
                             struct shash *meter_groups, struct ds *match,
-                            struct ds *actions, enum ovn_stage stage)
+                            struct ds *actions, enum ovn_stage stage,
+                            struct ovn_port *outport)
 {
+    char *outport_match = NULL;
+    if (outport) {
+        outport_match = xasprintf("outport == %s && ", outport->json_key);
+    }
+
     if (op->lrp_networks.ipv4_addrs) {
         ds_clear(match);
-        ds_put_format(match,
-                      "inport == %s && ip4 && "REGBIT_PKT_LARGER
-                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key);
+        ds_put_format(match, "inport == %s && %sip4 && "REGBIT_PKT_LARGER
+                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
+                      outport ? outport_match : "");
 
         ds_clear(actions);
         /* Set icmp4.frag_mtu to gw_mtu */
@@ -10931,8 +10937,9 @@  build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
 
     if (op->lrp_networks.ipv6_addrs) {
         ds_clear(match);
-        ds_put_format(match, "inport == %s && ip6 && "REGBIT_PKT_LARGER
-                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key);
+        ds_put_format(match, "inport == %s && %sip6 && "REGBIT_PKT_LARGER
+                      " && "REGBIT_EGRESS_LOOPBACK" == 0", op->json_key,
+                      outport ? outport_match : "");
 
         ds_clear(actions);
         /* Set icmp6.frag_mtu to gw_mtu */
@@ -10960,6 +10967,9 @@  build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
                                         meter_groups),
                                   &op->nbrp->header_);
     }
+    if (outport) {
+        free(outport_match);
+    }
 }
 
 static int
@@ -10999,7 +11009,8 @@  build_check_pkt_len_flows_for_lrp(struct ovn_port *op,
 
     /* ingress traffic */
     build_icmperr_pkt_big_flows(op, gw_mtu, lflows, meter_groups,
-                                match, actions, S_ROUTER_IN_IP_INPUT);
+                                match, actions, S_ROUTER_IN_IP_INPUT,
+                                NULL);
 
     for (size_t i = 0; i < op->od->nbr->n_ports; i++) {
         struct ovn_port *rp = ovn_port_find(ports,
@@ -11010,7 +11021,8 @@  build_check_pkt_len_flows_for_lrp(struct ovn_port *op,
 
         /* egress traffic */
         build_icmperr_pkt_big_flows(rp, gw_mtu, lflows, meter_groups,
-                                    match, actions, S_ROUTER_IN_LARGER_PKTS);
+                                    match, actions, S_ROUTER_IN_LARGER_PKTS,
+                                    op);
     }
 }
 
@@ -11044,21 +11056,14 @@  build_check_pkt_len_flows_for_lrouter(
     ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 0, "1",
                   "next;");
 
-    if (od->l3dgw_port && od->l3redirect_port) {
-        /* gw router port */
-        build_check_pkt_len_flows_for_lrp(od->l3dgw_port, lflows,
-                                          ports, meter_groups, match, actions);
-    } else if (smap_get(&od->nbr->options, "chassis")) {
-        for (size_t i = 0; i < od->nbr->n_ports; i++) {
-            /* gw router */
-            struct ovn_port *rp = ovn_port_find(ports,
-                                                od->nbr->ports[i]->name);
-            if (!rp) {
-                continue;
-            }
-            build_check_pkt_len_flows_for_lrp(rp, lflows, ports, meter_groups,
-                                              match, actions);
+    for (size_t i = 0; i < od->nbr->n_ports; i++) {
+        struct ovn_port *rp = ovn_port_find(ports,
+                                            od->nbr->ports[i]->name);
+        if (!rp || !rp->nbrp) {
+            continue;
         }
+        build_check_pkt_len_flows_for_lrp(rp, lflows, ports, meter_groups,
+                                          match, actions);
     }
 }
 
diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
index 091fe10b3..d7141294e 100644
--- a/northd/ovn_northd.dl
+++ b/northd/ovn_northd.dl
@@ -7508,29 +7508,16 @@  for (&Router(._uuid = lr_uuid))
 
 /* Local router ingress table CHK_PKT_LEN: Check packet length.
  *
- * For distributed routers with gateway ports.
- * Any IPv4 or IPv6 packet with outport set to the distributed gateway
- * router port, check the packet length and store the result in the
- * 'REGBIT_PKT_LARGER' register bit.
+ * Any IPv4 or IPv6 packet with outport set to a router port that has
+ * gateway_mtu > 0 configured, check the packet length and store the result in
+ * the 'REGBIT_PKT_LARGER' register bit.
  *
  * Local router ingress table LARGER_PKTS: Handle larger packets.
  *
- * Any IPv4 or IPv6 packet with outport set to the distributed gateway
- * router port and the 'REGBIT_PKT_LARGER' register bit is set,
+ * Any IPv4 or IPv6 packet with outport set to a router port that has
+ * gatway_mtu > 0 configured and the 'REGBIT_PKT_LARGER' register bit is set,
  * generate an ICMPv4/ICMPv6 packet with type 3/2 (Destination
  * Unreachable/Packet Too Big) and code 4/0 (Fragmentation needed).
- *
- * For Gateway routers.
- * Any IPv4 or IPv6 packet with outport set to the router port which has
- * the option 'gateway_mtu' set, check the packet length and store
- * the result in the 'REGBIT_PKT_LARGER' register bit.
- *
- * Local router ingress table LARGER_PKTS: Handle larger packets.
- *
- * Any IPv4 or IPv6 packet with outport set to the router port which has
- * the option 'gateway_mtu' set and the 'REGBIT_PKT_LARGER' register bit
- * is set, generate ICMPv4/ICMPv6 packet with type 3/2 (Destination
- * Unreachable/Packet Too Big) and * code 4/0 (Fragmentation needed).
  */
 Flow(.logical_datapath = lr_uuid,
      .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
@@ -7549,21 +7536,19 @@  Flow(.logical_datapath = lr_uuid,
 Flow(.logical_datapath = lr_uuid,
      .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
      .priority         = 50,
-     .__match          = "outport == ${l3dgw_port_json_name}",
+     .__match          = "outport == ${gw_mtu_rp.json_name}",
      .actions          = "${rEGBIT_PKT_LARGER()} = check_pkt_larger(${mtu}); "
                          "next;",
-     .external_ids     = stage_hint(l3dgw_port._uuid)) :-
+     .external_ids     = stage_hint(gw_mtu_rp.lrp._uuid)) :-
     r in &Router(._uuid = lr_uuid),
-    Some{var l3dgw_port} = r.l3dgw_port,
-    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
-    r.redirect_port_name != "",
-    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
+    gw_mtu_rp in &RouterPort(.router = r),
+    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
     gw_mtu > 0,
     var mtu = gw_mtu + vLAN_ETH_HEADER_LEN().
 MeteredFlow(.logical_datapath = lr_uuid,
             .stage            = s_ROUTER_IN_LARGER_PKTS(),
             .priority         = 150,
-            .__match          = "inport == ${rp.json_name} && ip4 && "
+            .__match          = "inport == ${rp.json_name} && outport == ${gw_mtu_rp.json_name} && ip4 && "
                                 "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
             .actions          = "icmp4_error {"
                                 "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
@@ -7582,13 +7567,12 @@  MeteredFlow(.logical_datapath = lr_uuid,
             .controller_meter = r.copp.get(cOPP_ICMP4_ERR()),
             .external_ids     = stage_hint(rp.lrp._uuid)) :-
     r in &Router(._uuid = lr_uuid),
-    Some{var l3dgw_port} = r.l3dgw_port,
-    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
-    r.redirect_port_name != "",
-    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
+    gw_mtu_rp in &RouterPort(.router = r),
+    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
     gw_mtu > 0,
+    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
     rp in &RouterPort(.router = r),
-    rp.lrp != l3dgw_port,
+    rp.lrp != gw_mtu_rp.lrp,
     Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
 
 MeteredFlow(.logical_datapath = lr_uuid,
@@ -7613,19 +7597,18 @@  MeteredFlow(.logical_datapath = lr_uuid,
             .controller_meter = r.copp.get(cOPP_ICMP4_ERR()),
             .external_ids     = stage_hint(rp.lrp._uuid)) :-
     r in &Router(._uuid = lr_uuid),
-    Some{var l3dgw_port} = r.l3dgw_port,
-    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
-    r.redirect_port_name != "",
-    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
+    gw_mtu_rp in &RouterPort(.router = r),
+    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
     gw_mtu > 0,
+    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
     rp in &RouterPort(.router = r),
-    rp.lrp == l3dgw_port,
+    rp.lrp == gw_mtu_rp.lrp,
     Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
 
 MeteredFlow(.logical_datapath = lr_uuid,
             .stage            = s_ROUTER_IN_LARGER_PKTS(),
             .priority         = 150,
-            .__match          = "inport == ${rp.json_name} && ip6 && "
+            .__match          = "inport == ${rp.json_name} && outport == ${gw_mtu_rp.json_name} && ip6 && "
                                 "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
             .actions          = "icmp6_error {"
                                 "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
@@ -7644,13 +7627,12 @@  MeteredFlow(.logical_datapath = lr_uuid,
             .controller_meter = r.copp.get(cOPP_ICMP6_ERR()),
             .external_ids     = stage_hint(rp.lrp._uuid)) :-
     r in &Router(._uuid = lr_uuid),
-    Some{var l3dgw_port} = r.l3dgw_port,
-    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
-    r.redirect_port_name != "",
-    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
+    gw_mtu_rp in &RouterPort(.router = r),
+    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
     gw_mtu > 0,
+    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
     rp in &RouterPort(.router = r),
-    rp.lrp != l3dgw_port,
+    rp.lrp != gw_mtu_rp.lrp,
     Some{var first_ipv6} = rp.networks.ipv6_addrs.nth(0).
 
 MeteredFlow(.logical_datapath = lr_uuid,
@@ -7675,137 +7657,6 @@  MeteredFlow(.logical_datapath = lr_uuid,
             .controller_meter = r.copp.get(cOPP_ICMP6_ERR()),
             .external_ids     = stage_hint(rp.lrp._uuid)) :-
     r in &Router(._uuid = lr_uuid),
-    Some{var l3dgw_port} = r.l3dgw_port,
-    var l3dgw_port_json_name = json_string_escape(l3dgw_port.name),
-    r.redirect_port_name != "",
-    var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0),
-    gw_mtu > 0,
-    rp in &RouterPort(.router = r),
-    rp.lrp == l3dgw_port,
-    Some{var first_ipv6} = rp.networks.ipv6_addrs.nth(0).
-
-/* Gateway routers. */
-Flow(.logical_datapath = lr_uuid,
-     .stage            = s_ROUTER_IN_CHK_PKT_LEN(),
-     .priority         = 50,
-     .__match          = "outport == ${gw_mtu_rp.json_name}",
-     .actions          = "${rEGBIT_PKT_LARGER()} = check_pkt_larger(${mtu}); "
-                         "next;",
-     .external_ids     = stage_hint(gw_mtu_rp.lrp._uuid)) :-
-    r in &Router(._uuid = lr_uuid),
-    r.is_gateway,
-    gw_mtu_rp in &RouterPort(.router = r),
-    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
-    gw_mtu > 0,
-    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN().
-Flow(.logical_datapath = lr_uuid,
-     .stage            = s_ROUTER_IN_LARGER_PKTS(),
-     .priority         = 150,
-     .__match          = "inport == ${rp.json_name} && ip4 && "
-                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
-     .actions          = "icmp4_error {"
-                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
-                         "${rEGBIT_PKT_LARGER()} = 0; "
-                         "eth.dst = ${rp.networks.ea}; "
-                         "ip4.dst = ip4.src; "
-                         "ip4.src = ${first_ipv4.addr}; "
-                         "ip.ttl = 255; "
-                         "icmp4.type = 3; /* Destination Unreachable. */ "
-                         "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
-                         /* Set icmp4.frag_mtu to gw_mtu */
-                         "icmp4.frag_mtu = ${gw_mtu}; "
-                         "next(pipeline=ingress, table=0); "
-                         "};",
-     .external_ids     = stage_hint(rp.lrp._uuid)) :-
-    r in &Router(._uuid = lr_uuid),
-    r.is_gateway,
-    gw_mtu_rp in &RouterPort(.router = r),
-    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
-    gw_mtu > 0,
-    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
-    rp in &RouterPort(.router = r),
-    rp.lrp != gw_mtu_rp.lrp,
-    Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
-
-Flow(.logical_datapath = lr_uuid,
-     .stage            = s_ROUTER_IN_IP_INPUT(),
-     .priority         = 150,
-     .__match          = "inport == ${rp.json_name} && ip4 && "
-                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
-     .actions          = "icmp4_error {"
-                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
-                         "${rEGBIT_PKT_LARGER()} = 0; "
-                         "eth.dst = ${rp.networks.ea}; "
-                         "ip4.dst = ip4.src; "
-                         "ip4.src = ${first_ipv4.addr}; "
-                         "ip.ttl = 255; "
-                         "icmp4.type = 3; /* Destination Unreachable. */ "
-                         "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
-                         /* Set icmp4.frag_mtu to gw_mtu */
-                         "icmp4.frag_mtu = ${gw_mtu}; "
-                         "next(pipeline=ingress, table=0); "
-                         "};",
-     .external_ids     = stage_hint(rp.lrp._uuid)) :-
-    r in &Router(._uuid = lr_uuid),
-    r.is_gateway,
-    gw_mtu_rp in &RouterPort(.router = r),
-    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
-    gw_mtu > 0,
-    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
-    rp in &RouterPort(.router = r),
-    rp.lrp == gw_mtu_rp.lrp,
-    Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0).
-
-Flow(.logical_datapath = lr_uuid,
-     .stage            = s_ROUTER_IN_LARGER_PKTS(),
-     .priority         = 150,
-     .__match          = "inport == ${rp.json_name} && ip6 && "
-                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
-     .actions          = "icmp6_error {"
-                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
-                         "${rEGBIT_PKT_LARGER()} = 0; "
-                         "eth.dst = ${rp.networks.ea}; "
-                         "ip6.dst = ip6.src; "
-                         "ip6.src = ${first_ipv6.addr}; "
-                         "ip.ttl = 255; "
-                         "icmp6.type = 2; /* Packet Too Big. */ "
-                         "icmp6.code = 0; "
-                         /* Set icmp6.frag_mtu to gw_mtu */
-                         "icmp6.frag_mtu = ${gw_mtu}; "
-                         "next(pipeline=ingress, table=0); "
-                         "};",
-     .external_ids     = stage_hint(rp.lrp._uuid)) :-
-    r in &Router(._uuid = lr_uuid),
-    r.is_gateway,
-    gw_mtu_rp in &RouterPort(.router = r),
-    var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
-    gw_mtu > 0,
-    var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(),
-    rp in &RouterPort(.router = r),
-    rp.lrp != gw_mtu_rp.lrp,
-    Some{var first_ipv6} = rp.networks.ipv6_addrs.nth(0).
-
-Flow(.logical_datapath = lr_uuid,
-     .stage            = s_ROUTER_IN_IP_INPUT(),
-     .priority         = 150,
-     .__match          = "inport == ${rp.json_name} && ip6 && "
-                         "${rEGBIT_PKT_LARGER()} && ${rEGBIT_EGRESS_LOOPBACK()} == 0",
-     .actions          = "icmp6_error {"
-                         "${rEGBIT_EGRESS_LOOPBACK()} = 1; "
-                         "${rEGBIT_PKT_LARGER()} = 0; "
-                         "eth.dst = ${rp.networks.ea}; "
-                         "ip6.dst = ip6.src; "
-                         "ip6.src = ${first_ipv6.addr}; "
-                         "ip.ttl = 255; "
-                         "icmp6.type = 2; /* Packet Too Big. */ "
-                         "icmp6.code = 0; "
-                         /* Set icmp6.frag_mtu to gw_mtu */
-                         "icmp6.frag_mtu = ${gw_mtu}; "
-                         "next(pipeline=ingress, table=0); "
-                         "};",
-     .external_ids     = stage_hint(rp.lrp._uuid)) :-
-    r in &Router(._uuid = lr_uuid),
-    r.is_gateway,
     gw_mtu_rp in &RouterPort(.router = r),
     var gw_mtu = gw_mtu_rp.lrp.options.get_int_def("gateway_mtu", 0),
     gw_mtu > 0,
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 474e88021..2098b1c19 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -4865,10 +4865,10 @@  AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
   table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
   table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
   table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
-  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && 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=16(lr_in_larger_pkts  ), 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 = 1500; next(pipeline=ingress, table=0); };)
-  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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); };)
 ])
 
 AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
@@ -4886,14 +4886,6 @@  AT_CHECK([grep -E "lr_in_ip_input.*icmp6_error" lr0flows | sort], [0], [dnl
 # Clear the gateway-chassis for lr0-public
 check ovn-nbctl --wait=sb clear logical_router_port lr0-public gateway_chassis
 
-ovn-sbctl dump-flows lr0 > lr0flows
-AT_CAPTURE_FILE([sw0flows])
-
-AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [dnl
-  table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
-  table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
-])
-
 # Make lr0 as a gateway router.
 check ovn-nbctl --wait=sb set logical_router lr0 options:chassis=ch1
 
@@ -4904,10 +4896,10 @@  AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
   table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
   table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
   table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
-  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && 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=16(lr_in_larger_pkts  ), 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 = 1500; next(pipeline=ingress, table=0); };)
-  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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); };)
 ])
 
 AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
@@ -4933,14 +4925,14 @@  AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
   table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
   table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); next;)
   table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
-  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && 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=16(lr_in_larger_pkts  ), 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 = 1400; next(pipeline=ingress, table=0); };)
-  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && 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=16(lr_in_larger_pkts  ), 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 = 1500; next(pipeline=ingress, table=0); };)
-  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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=16(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=16(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=16(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=16(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); };)
 ])
 
 AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
@@ -4968,10 +4960,10 @@  AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sort], [0], [d
   table=15(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
   table=15(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); next;)
   table=16(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
-  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && 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=16(lr_in_larger_pkts  ), 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 = 1400; next(pipeline=ingress, table=0); };)
-  table=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && 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=16(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=16(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=16(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=16(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); };)
 ])
 
 AT_CLEANUP