diff mbox series

[ovs-dev,v3,2/4] ovn-northd: Move NAT ARP/ND resolution to separate functions.

Message ID 20200917125102.19729.95989.stgit@dceara.remote.csb
State Accepted
Headers show
Series Drop packets destined to own IPs and refactor SNAT processing. | expand

Commit Message

Dumitru Ceara Sept. 17, 2020, 12:51 p.m. UTC
To avoid duplicating code later on, move out the code that generates ARP/ND
replies for NAT external IPs to separate functions.

Signed-off-by: Dumitru Ceara <dceara@redhat.com>
---
 northd/ovn-northd.c |  172 ++++++++++++++++++++++++++++-----------------------
 1 file changed, 94 insertions(+), 78 deletions(-)

Comments

Han Zhou Sept. 21, 2020, 6:53 a.m. UTC | #1
On Thu, Sep 17, 2020 at 5:51 AM Dumitru Ceara <dceara@redhat.com> wrote:
>
> To avoid duplicating code later on, move out the code that generates
ARP/ND
> replies for NAT external IPs to separate functions.
>
> Signed-off-by: Dumitru Ceara <dceara@redhat.com>
> ---
>  northd/ovn-northd.c |  172
++++++++++++++++++++++++++++-----------------------
>  1 file changed, 94 insertions(+), 78 deletions(-)
>
> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index d5d7631..f79ed99 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -8636,6 +8636,94 @@ build_lrouter_nd_flow(struct ovn_datapath *od,
struct ovn_port *op,
>  }
>
>  static void
> +build_lrouter_nat_arp_nd_flow(struct ovn_datapath *od,
> +                              struct ovn_nat *nat_entry,
> +                              struct hmap *lflows)
> +{
> +    struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
> +    const struct nbrec_nat *nat = nat_entry->nb;
> +
> +    if (nat_entry_is_v6(nat_entry)) {
> +        build_lrouter_nd_flow(od, NULL, "nd_na",
> +                              ext_addrs->ipv6_addrs[0].addr_s,
> +                              ext_addrs->ipv6_addrs[0].sn_addr_s,
> +                              REG_INPORT_ETH_ADDR, NULL, false, 90,
> +                              &nat->header_, lflows);
> +    } else {
> +        build_lrouter_arp_flow(od, NULL,
> +                               ext_addrs->ipv4_addrs[0].addr_s,
> +                               REG_INPORT_ETH_ADDR, NULL, false, 90,
> +                               &nat->header_, lflows);
> +    }
> +}
> +
> +static void
> +build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
> +                                   struct ovn_nat *nat_entry,
> +                                   struct hmap *lflows)
> +{
> +    struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
> +    const struct nbrec_nat *nat = nat_entry->nb;
> +    struct ds match = DS_EMPTY_INITIALIZER;
> +
> +    /* Mac address to use when replying to ARP/NS. */
> +    const char *mac_s = REG_INPORT_ETH_ADDR;
> +    struct eth_addr mac;
> +
> +    if (nat->external_mac &&
> +        eth_addr_from_string(nat->external_mac, &mac)
> +        && nat->logical_port) {
> +        /* distributed NAT case, use nat->external_mac */
> +        mac_s = nat->external_mac;
> +        /* Traffic with eth.src = nat->external_mac should only be
> +         * sent from the chassis where nat->logical_port is
> +         * resident, so that upstream MAC learning points to the
> +         * correct chassis.  Also need to avoid generation of
> +         * multiple ARP responses from different chassis. */
> +        ds_put_format(&match, "is_chassis_resident(\"%s\")",
> +                      nat->logical_port);
> +    } else {
> +        mac_s = REG_INPORT_ETH_ADDR;
> +        /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
> +         * should only be sent from the "redirect-chassis", so that
> +         * upstream MAC learning points to the "redirect-chassis".
> +         * Also need to avoid generation of multiple ARP responses
> +         * from different chassis. */
> +        if (op->od->l3redirect_port) {
> +            ds_put_format(&match, "is_chassis_resident(%s)",
> +                          op->od->l3redirect_port->json_key);
> +        }
> +    }
> +
> +    /* Respond to ARP/NS requests on the chassis that binds the gw
> +     * port. Drop the ARP/NS requests on other chassis.
> +     */
> +    if (nat_entry_is_v6(nat_entry)) {
> +        build_lrouter_nd_flow(op->od, op, "nd_na",
> +                              ext_addrs->ipv6_addrs[0].addr_s,
> +                              ext_addrs->ipv6_addrs[0].sn_addr_s,
> +                              mac_s, &match, false, 92,
> +                              &nat->header_, lflows);
> +        build_lrouter_nd_flow(op->od, op, "nd_na",
> +                              ext_addrs->ipv6_addrs[0].addr_s,
> +                              ext_addrs->ipv6_addrs[0].sn_addr_s,
> +                              mac_s, NULL, true, 91,
> +                              &nat->header_, lflows);
> +    } else {
> +        build_lrouter_arp_flow(op->od, op,
> +                               ext_addrs->ipv4_addrs[0].addr_s,
> +                               mac_s, &match, false, 92,
> +                               &nat->header_, lflows);
> +        build_lrouter_arp_flow(op->od, op,
> +                               ext_addrs->ipv4_addrs[0].addr_s,
> +                               mac_s, NULL, true, 91,
> +                               &nat->header_, lflows);
> +    }
> +
> +    ds_destroy(&match);
> +}
> +
> +static void
>  build_lrouter_force_snat_flows(struct hmap *lflows, struct ovn_datapath
*od,
>                                 const char *ip_version, const char
*ip_addr,
>                                 const char *context)
> @@ -8869,6 +8957,10 @@ build_lrouter_flows(struct hmap *datapaths, struct
hmap *ports,
>          /* Priority-90-92 flows handle ARP requests and ND packets. Most
are
>           * per logical port but DNAT addresses can be handled per
datapath
>           * for non gateway router ports.
> +         *
> +         * Priority 91 and 92 flows are added for each gateway router
> +         * port to handle the special cases. In case we get the packet
> +         * on a regular port, just reply with the port's ETH address.
>           */
>          struct sset snat_ips = SSET_INITIALIZER(&snat_ips);
>          for (int i = 0; i < od->nbr->n_nat; i++) {
> @@ -8890,23 +8982,7 @@ build_lrouter_flows(struct hmap *datapaths, struct
hmap *ports,
>                      continue;
>                  }
>              }
> -
> -            /* Priority 91 and 92 flows are added for each gateway router
> -             * port to handle the special cases. In case we get the
packet
> -             * on a regular port, just reply with the port's ETH address.
> -             */
> -            if (nat_entry_is_v6(nat_entry)) {
> -                build_lrouter_nd_flow(od, NULL, "nd_na",
> -                                      ext_addrs->ipv6_addrs[0].addr_s,
> -                                      ext_addrs->ipv6_addrs[0].sn_addr_s,
> -                                      REG_INPORT_ETH_ADDR, NULL, false,
90,
> -                                      &nat->header_, lflows);
> -            } else {
> -                build_lrouter_arp_flow(od, NULL,
> -                                       ext_addrs->ipv4_addrs[0].addr_s,
> -                                       REG_INPORT_ETH_ADDR, NULL, false,
90,
> -                                       &nat->header_, lflows);
> -            }
> +            build_lrouter_nat_arp_nd_flow(od, nat_entry, lflows);
>          }
>          sset_destroy(&snat_ips);
>
> @@ -9212,67 +9288,7 @@ build_lrouter_flows(struct hmap *datapaths, struct
hmap *ports,
>                      continue;
>                  }
>              }
> -
> -            /* Mac address to use when replying to ARP/NS. */
> -            const char *mac_s = REG_INPORT_ETH_ADDR;
> -
> -            /* ARP / ND handling for external IP addresses.
> -             *
> -             * DNAT IP addresses are external IP addresses that need ARP
> -             * handling. */
> -
> -            struct eth_addr mac;
> -
> -            ds_clear(&match);
> -            if (nat->external_mac &&
> -                eth_addr_from_string(nat->external_mac, &mac)
> -                && nat->logical_port) {
> -                /* distributed NAT case, use nat->external_mac */
> -                mac_s = nat->external_mac;
> -                /* Traffic with eth.src = nat->external_mac should only
be
> -                 * sent from the chassis where nat->logical_port is
> -                 * resident, so that upstream MAC learning points to the
> -                 * correct chassis.  Also need to avoid generation of
> -                 * multiple ARP responses from different chassis. */
> -                ds_put_format(&match, "is_chassis_resident(\"%s\")",
> -                              nat->logical_port);
> -            } else {
> -                mac_s = REG_INPORT_ETH_ADDR;
> -                /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
> -                 * should only be sent from the "redirect-chassis", so
that
> -                 * upstream MAC learning points to the
"redirect-chassis".
> -                 * Also need to avoid generation of multiple ARP
responses
> -                 * from different chassis. */
> -                if (op->od->l3redirect_port) {
> -                    ds_put_format(&match, "is_chassis_resident(%s)",
> -                                  op->od->l3redirect_port->json_key);
> -                }
> -            }
> -
> -            /* Respond to ARP/NS requests on the chassis that binds the
gw
> -             * port. Drop the ARP/NS requests on other chassis.
> -             */
> -            if (nat_entry_is_v6(nat_entry)) {
> -                build_lrouter_nd_flow(op->od, op, "nd_na",
> -                                      ext_addrs->ipv6_addrs[0].addr_s,
> -                                      ext_addrs->ipv6_addrs[0].sn_addr_s,
> -                                      mac_s, &match, false, 92,
> -                                      &nat->header_, lflows);
> -                build_lrouter_nd_flow(op->od, op, "nd_na",
> -                                      ext_addrs->ipv6_addrs[0].addr_s,
> -                                      ext_addrs->ipv6_addrs[0].sn_addr_s,
> -                                      mac_s, NULL, true, 91,
> -                                      &nat->header_, lflows);
> -            } else {
> -                build_lrouter_arp_flow(op->od, op,
> -                                       ext_addrs->ipv4_addrs[0].addr_s,
> -                                       mac_s, &match, false, 92,
> -                                       &nat->header_, lflows);
> -                build_lrouter_arp_flow(op->od, op,
> -                                       ext_addrs->ipv4_addrs[0].addr_s,
> -                                       mac_s, NULL, true, 91,
> -                                       &nat->header_, lflows);
> -            }
> +            build_lrouter_port_nat_arp_nd_flow(op, nat_entry, lflows);
>          }
>          sset_destroy(&sset_snat_ips);
>      }
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Acked-by: Han Zhou <hzhou@ovn.org>
Numan Siddique Sept. 22, 2020, 6:18 a.m. UTC | #2
On Mon, Sep 21, 2020 at 12:23 PM Han Zhou <hzhou@ovn.org> wrote:

> On Thu, Sep 17, 2020 at 5:51 AM Dumitru Ceara <dceara@redhat.com> wrote:
> >
> > To avoid duplicating code later on, move out the code that generates
> ARP/ND
> > replies for NAT external IPs to separate functions.
> >
> > Signed-off-by: Dumitru Ceara <dceara@redhat.com>
> > ---
> >  northd/ovn-northd.c |  172
> ++++++++++++++++++++++++++++-----------------------
> >  1 file changed, 94 insertions(+), 78 deletions(-)
> >
> > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > index d5d7631..f79ed99 100644
> > --- a/northd/ovn-northd.c
> > +++ b/northd/ovn-northd.c
> > @@ -8636,6 +8636,94 @@ build_lrouter_nd_flow(struct ovn_datapath *od,
> struct ovn_port *op,
> >  }
> >
> >  static void
> > +build_lrouter_nat_arp_nd_flow(struct ovn_datapath *od,
> > +                              struct ovn_nat *nat_entry,
> > +                              struct hmap *lflows)
> > +{
> > +    struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
> > +    const struct nbrec_nat *nat = nat_entry->nb;
> > +
> > +    if (nat_entry_is_v6(nat_entry)) {
> > +        build_lrouter_nd_flow(od, NULL, "nd_na",
> > +                              ext_addrs->ipv6_addrs[0].addr_s,
> > +                              ext_addrs->ipv6_addrs[0].sn_addr_s,
> > +                              REG_INPORT_ETH_ADDR, NULL, false, 90,
> > +                              &nat->header_, lflows);
> > +    } else {
> > +        build_lrouter_arp_flow(od, NULL,
> > +                               ext_addrs->ipv4_addrs[0].addr_s,
> > +                               REG_INPORT_ETH_ADDR, NULL, false, 90,
> > +                               &nat->header_, lflows);
> > +    }
> > +}
> > +
> > +static void
> > +build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
> > +                                   struct ovn_nat *nat_entry,
> > +                                   struct hmap *lflows)
> > +{
> > +    struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
> > +    const struct nbrec_nat *nat = nat_entry->nb;
> > +    struct ds match = DS_EMPTY_INITIALIZER;
> > +
> > +    /* Mac address to use when replying to ARP/NS. */
> > +    const char *mac_s = REG_INPORT_ETH_ADDR;
> > +    struct eth_addr mac;
> > +
> > +    if (nat->external_mac &&
> > +        eth_addr_from_string(nat->external_mac, &mac)
> > +        && nat->logical_port) {
> > +        /* distributed NAT case, use nat->external_mac */
> > +        mac_s = nat->external_mac;
> > +        /* Traffic with eth.src = nat->external_mac should only be
> > +         * sent from the chassis where nat->logical_port is
> > +         * resident, so that upstream MAC learning points to the
> > +         * correct chassis.  Also need to avoid generation of
> > +         * multiple ARP responses from different chassis. */
> > +        ds_put_format(&match, "is_chassis_resident(\"%s\")",
> > +                      nat->logical_port);
> > +    } else {
> > +        mac_s = REG_INPORT_ETH_ADDR;
> > +        /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
> > +         * should only be sent from the "redirect-chassis", so that
> > +         * upstream MAC learning points to the "redirect-chassis".
> > +         * Also need to avoid generation of multiple ARP responses
> > +         * from different chassis. */
> > +        if (op->od->l3redirect_port) {
> > +            ds_put_format(&match, "is_chassis_resident(%s)",
> > +                          op->od->l3redirect_port->json_key);
> > +        }
> > +    }
> > +
> > +    /* Respond to ARP/NS requests on the chassis that binds the gw
> > +     * port. Drop the ARP/NS requests on other chassis.
> > +     */
> > +    if (nat_entry_is_v6(nat_entry)) {
> > +        build_lrouter_nd_flow(op->od, op, "nd_na",
> > +                              ext_addrs->ipv6_addrs[0].addr_s,
> > +                              ext_addrs->ipv6_addrs[0].sn_addr_s,
> > +                              mac_s, &match, false, 92,
> > +                              &nat->header_, lflows);
> > +        build_lrouter_nd_flow(op->od, op, "nd_na",
> > +                              ext_addrs->ipv6_addrs[0].addr_s,
> > +                              ext_addrs->ipv6_addrs[0].sn_addr_s,
> > +                              mac_s, NULL, true, 91,
> > +                              &nat->header_, lflows);
> > +    } else {
> > +        build_lrouter_arp_flow(op->od, op,
> > +                               ext_addrs->ipv4_addrs[0].addr_s,
> > +                               mac_s, &match, false, 92,
> > +                               &nat->header_, lflows);
> > +        build_lrouter_arp_flow(op->od, op,
> > +                               ext_addrs->ipv4_addrs[0].addr_s,
> > +                               mac_s, NULL, true, 91,
> > +                               &nat->header_, lflows);
> > +    }
> > +
> > +    ds_destroy(&match);
> > +}
> > +
> > +static void
> >  build_lrouter_force_snat_flows(struct hmap *lflows, struct ovn_datapath
> *od,
> >                                 const char *ip_version, const char
> *ip_addr,
> >                                 const char *context)
> > @@ -8869,6 +8957,10 @@ build_lrouter_flows(struct hmap *datapaths, struct
> hmap *ports,
> >          /* Priority-90-92 flows handle ARP requests and ND packets. Most
> are
> >           * per logical port but DNAT addresses can be handled per
> datapath
> >           * for non gateway router ports.
> > +         *
> > +         * Priority 91 and 92 flows are added for each gateway router
> > +         * port to handle the special cases. In case we get the packet
> > +         * on a regular port, just reply with the port's ETH address.
> >           */
> >          struct sset snat_ips = SSET_INITIALIZER(&snat_ips);
> >          for (int i = 0; i < od->nbr->n_nat; i++) {
> > @@ -8890,23 +8982,7 @@ build_lrouter_flows(struct hmap *datapaths, struct
> hmap *ports,
> >                      continue;
> >                  }
> >              }
> > -
> > -            /* Priority 91 and 92 flows are added for each gateway
> router
> > -             * port to handle the special cases. In case we get the
> packet
> > -             * on a regular port, just reply with the port's ETH
> address.
> > -             */
> > -            if (nat_entry_is_v6(nat_entry)) {
> > -                build_lrouter_nd_flow(od, NULL, "nd_na",
> > -                                      ext_addrs->ipv6_addrs[0].addr_s,
> > -
> ext_addrs->ipv6_addrs[0].sn_addr_s,
> > -                                      REG_INPORT_ETH_ADDR, NULL, false,
> 90,
> > -                                      &nat->header_, lflows);
> > -            } else {
> > -                build_lrouter_arp_flow(od, NULL,
> > -                                       ext_addrs->ipv4_addrs[0].addr_s,
> > -                                       REG_INPORT_ETH_ADDR, NULL, false,
> 90,
> > -                                       &nat->header_, lflows);
> > -            }
> > +            build_lrouter_nat_arp_nd_flow(od, nat_entry, lflows);
> >          }
> >          sset_destroy(&snat_ips);
> >
> > @@ -9212,67 +9288,7 @@ build_lrouter_flows(struct hmap *datapaths, struct
> hmap *ports,
> >                      continue;
> >                  }
> >              }
> > -
> > -            /* Mac address to use when replying to ARP/NS. */
> > -            const char *mac_s = REG_INPORT_ETH_ADDR;
> > -
> > -            /* ARP / ND handling for external IP addresses.
> > -             *
> > -             * DNAT IP addresses are external IP addresses that need ARP
> > -             * handling. */
> > -
> > -            struct eth_addr mac;
> > -
> > -            ds_clear(&match);
> > -            if (nat->external_mac &&
> > -                eth_addr_from_string(nat->external_mac, &mac)
> > -                && nat->logical_port) {
> > -                /* distributed NAT case, use nat->external_mac */
> > -                mac_s = nat->external_mac;
> > -                /* Traffic with eth.src = nat->external_mac should only
> be
> > -                 * sent from the chassis where nat->logical_port is
> > -                 * resident, so that upstream MAC learning points to the
> > -                 * correct chassis.  Also need to avoid generation of
> > -                 * multiple ARP responses from different chassis. */
> > -                ds_put_format(&match, "is_chassis_resident(\"%s\")",
> > -                              nat->logical_port);
> > -            } else {
> > -                mac_s = REG_INPORT_ETH_ADDR;
> > -                /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
> > -                 * should only be sent from the "redirect-chassis", so
> that
> > -                 * upstream MAC learning points to the
> "redirect-chassis".
> > -                 * Also need to avoid generation of multiple ARP
> responses
> > -                 * from different chassis. */
> > -                if (op->od->l3redirect_port) {
> > -                    ds_put_format(&match, "is_chassis_resident(%s)",
> > -                                  op->od->l3redirect_port->json_key);
> > -                }
> > -            }
> > -
> > -            /* Respond to ARP/NS requests on the chassis that binds the
> gw
> > -             * port. Drop the ARP/NS requests on other chassis.
> > -             */
> > -            if (nat_entry_is_v6(nat_entry)) {
> > -                build_lrouter_nd_flow(op->od, op, "nd_na",
> > -                                      ext_addrs->ipv6_addrs[0].addr_s,
> > -
> ext_addrs->ipv6_addrs[0].sn_addr_s,
> > -                                      mac_s, &match, false, 92,
> > -                                      &nat->header_, lflows);
> > -                build_lrouter_nd_flow(op->od, op, "nd_na",
> > -                                      ext_addrs->ipv6_addrs[0].addr_s,
> > -
> ext_addrs->ipv6_addrs[0].sn_addr_s,
> > -                                      mac_s, NULL, true, 91,
> > -                                      &nat->header_, lflows);
> > -            } else {
> > -                build_lrouter_arp_flow(op->od, op,
> > -                                       ext_addrs->ipv4_addrs[0].addr_s,
> > -                                       mac_s, &match, false, 92,
> > -                                       &nat->header_, lflows);
> > -                build_lrouter_arp_flow(op->od, op,
> > -                                       ext_addrs->ipv4_addrs[0].addr_s,
> > -                                       mac_s, NULL, true, 91,
> > -                                       &nat->header_, lflows);
> > -            }
> > +            build_lrouter_port_nat_arp_nd_flow(op, nat_entry, lflows);
> >          }
> >          sset_destroy(&sset_snat_ips);
> >      }
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
> Acked-by: Han Zhou <hzhou@ovn.org>
>

Thanks Dumitru and Han. I applied this patch to master.

Numan


> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
diff mbox series

Patch

diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index d5d7631..f79ed99 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -8636,6 +8636,94 @@  build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op,
 }
 
 static void
+build_lrouter_nat_arp_nd_flow(struct ovn_datapath *od,
+                              struct ovn_nat *nat_entry,
+                              struct hmap *lflows)
+{
+    struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
+    const struct nbrec_nat *nat = nat_entry->nb;
+
+    if (nat_entry_is_v6(nat_entry)) {
+        build_lrouter_nd_flow(od, NULL, "nd_na",
+                              ext_addrs->ipv6_addrs[0].addr_s,
+                              ext_addrs->ipv6_addrs[0].sn_addr_s,
+                              REG_INPORT_ETH_ADDR, NULL, false, 90,
+                              &nat->header_, lflows);
+    } else {
+        build_lrouter_arp_flow(od, NULL,
+                               ext_addrs->ipv4_addrs[0].addr_s,
+                               REG_INPORT_ETH_ADDR, NULL, false, 90,
+                               &nat->header_, lflows);
+    }
+}
+
+static void
+build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op,
+                                   struct ovn_nat *nat_entry,
+                                   struct hmap *lflows)
+{
+    struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
+    const struct nbrec_nat *nat = nat_entry->nb;
+    struct ds match = DS_EMPTY_INITIALIZER;
+
+    /* Mac address to use when replying to ARP/NS. */
+    const char *mac_s = REG_INPORT_ETH_ADDR;
+    struct eth_addr mac;
+
+    if (nat->external_mac &&
+        eth_addr_from_string(nat->external_mac, &mac)
+        && nat->logical_port) {
+        /* distributed NAT case, use nat->external_mac */
+        mac_s = nat->external_mac;
+        /* Traffic with eth.src = nat->external_mac should only be
+         * sent from the chassis where nat->logical_port is
+         * resident, so that upstream MAC learning points to the
+         * correct chassis.  Also need to avoid generation of
+         * multiple ARP responses from different chassis. */
+        ds_put_format(&match, "is_chassis_resident(\"%s\")",
+                      nat->logical_port);
+    } else {
+        mac_s = REG_INPORT_ETH_ADDR;
+        /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
+         * should only be sent from the "redirect-chassis", so that
+         * upstream MAC learning points to the "redirect-chassis".
+         * Also need to avoid generation of multiple ARP responses
+         * from different chassis. */
+        if (op->od->l3redirect_port) {
+            ds_put_format(&match, "is_chassis_resident(%s)",
+                          op->od->l3redirect_port->json_key);
+        }
+    }
+
+    /* Respond to ARP/NS requests on the chassis that binds the gw
+     * port. Drop the ARP/NS requests on other chassis.
+     */
+    if (nat_entry_is_v6(nat_entry)) {
+        build_lrouter_nd_flow(op->od, op, "nd_na",
+                              ext_addrs->ipv6_addrs[0].addr_s,
+                              ext_addrs->ipv6_addrs[0].sn_addr_s,
+                              mac_s, &match, false, 92,
+                              &nat->header_, lflows);
+        build_lrouter_nd_flow(op->od, op, "nd_na",
+                              ext_addrs->ipv6_addrs[0].addr_s,
+                              ext_addrs->ipv6_addrs[0].sn_addr_s,
+                              mac_s, NULL, true, 91,
+                              &nat->header_, lflows);
+    } else {
+        build_lrouter_arp_flow(op->od, op,
+                               ext_addrs->ipv4_addrs[0].addr_s,
+                               mac_s, &match, false, 92,
+                               &nat->header_, lflows);
+        build_lrouter_arp_flow(op->od, op,
+                               ext_addrs->ipv4_addrs[0].addr_s,
+                               mac_s, NULL, true, 91,
+                               &nat->header_, lflows);
+    }
+
+    ds_destroy(&match);
+}
+
+static void
 build_lrouter_force_snat_flows(struct hmap *lflows, struct ovn_datapath *od,
                                const char *ip_version, const char *ip_addr,
                                const char *context)
@@ -8869,6 +8957,10 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
         /* Priority-90-92 flows handle ARP requests and ND packets. Most are
          * per logical port but DNAT addresses can be handled per datapath
          * for non gateway router ports.
+         *
+         * Priority 91 and 92 flows are added for each gateway router
+         * port to handle the special cases. In case we get the packet
+         * on a regular port, just reply with the port's ETH address.
          */
         struct sset snat_ips = SSET_INITIALIZER(&snat_ips);
         for (int i = 0; i < od->nbr->n_nat; i++) {
@@ -8890,23 +8982,7 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     continue;
                 }
             }
-
-            /* Priority 91 and 92 flows are added for each gateway router
-             * port to handle the special cases. In case we get the packet
-             * on a regular port, just reply with the port's ETH address.
-             */
-            if (nat_entry_is_v6(nat_entry)) {
-                build_lrouter_nd_flow(od, NULL, "nd_na",
-                                      ext_addrs->ipv6_addrs[0].addr_s,
-                                      ext_addrs->ipv6_addrs[0].sn_addr_s,
-                                      REG_INPORT_ETH_ADDR, NULL, false, 90,
-                                      &nat->header_, lflows);
-            } else {
-                build_lrouter_arp_flow(od, NULL,
-                                       ext_addrs->ipv4_addrs[0].addr_s,
-                                       REG_INPORT_ETH_ADDR, NULL, false, 90,
-                                       &nat->header_, lflows);
-            }
+            build_lrouter_nat_arp_nd_flow(od, nat_entry, lflows);
         }
         sset_destroy(&snat_ips);
 
@@ -9212,67 +9288,7 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     continue;
                 }
             }
-
-            /* Mac address to use when replying to ARP/NS. */
-            const char *mac_s = REG_INPORT_ETH_ADDR;
-
-            /* ARP / ND handling for external IP addresses.
-             *
-             * DNAT IP addresses are external IP addresses that need ARP
-             * handling. */
-
-            struct eth_addr mac;
-
-            ds_clear(&match);
-            if (nat->external_mac &&
-                eth_addr_from_string(nat->external_mac, &mac)
-                && nat->logical_port) {
-                /* distributed NAT case, use nat->external_mac */
-                mac_s = nat->external_mac;
-                /* Traffic with eth.src = nat->external_mac should only be
-                 * sent from the chassis where nat->logical_port is
-                 * resident, so that upstream MAC learning points to the
-                 * correct chassis.  Also need to avoid generation of
-                 * multiple ARP responses from different chassis. */
-                ds_put_format(&match, "is_chassis_resident(\"%s\")",
-                              nat->logical_port);
-            } else {
-                mac_s = REG_INPORT_ETH_ADDR;
-                /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
-                 * should only be sent from the "redirect-chassis", so that
-                 * upstream MAC learning points to the "redirect-chassis".
-                 * Also need to avoid generation of multiple ARP responses
-                 * from different chassis. */
-                if (op->od->l3redirect_port) {
-                    ds_put_format(&match, "is_chassis_resident(%s)",
-                                  op->od->l3redirect_port->json_key);
-                }
-            }
-
-            /* Respond to ARP/NS requests on the chassis that binds the gw
-             * port. Drop the ARP/NS requests on other chassis.
-             */
-            if (nat_entry_is_v6(nat_entry)) {
-                build_lrouter_nd_flow(op->od, op, "nd_na",
-                                      ext_addrs->ipv6_addrs[0].addr_s,
-                                      ext_addrs->ipv6_addrs[0].sn_addr_s,
-                                      mac_s, &match, false, 92,
-                                      &nat->header_, lflows);
-                build_lrouter_nd_flow(op->od, op, "nd_na",
-                                      ext_addrs->ipv6_addrs[0].addr_s,
-                                      ext_addrs->ipv6_addrs[0].sn_addr_s,
-                                      mac_s, NULL, true, 91,
-                                      &nat->header_, lflows);
-            } else {
-                build_lrouter_arp_flow(op->od, op,
-                                       ext_addrs->ipv4_addrs[0].addr_s,
-                                       mac_s, &match, false, 92,
-                                       &nat->header_, lflows);
-                build_lrouter_arp_flow(op->od, op,
-                                       ext_addrs->ipv4_addrs[0].addr_s,
-                                       mac_s, NULL, true, 91,
-                                       &nat->header_, lflows);
-            }
+            build_lrouter_port_nat_arp_nd_flow(op, nat_entry, lflows);
         }
         sset_destroy(&sset_snat_ips);
     }