diff mbox series

[ovs-dev,v3] northd: forward arp request to lrp snat on.

Message ID 20231205073356.665243-1-danieldin186@gmail.com
State Changes Requested
Delegated to: Dumitru Ceara
Headers show
Series [ovs-dev,v3] northd: forward arp request to lrp snat on. | expand

Checks

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

Commit Message

Daniel Ding Dec. 5, 2023, 7:33 a.m. UTC
If the router has a snat rule and it's external ip isn't lrp address,
when the arp request from other router for this external ip, will
be drop, because of this external ip use same mac address as lrp, so
can not forward to MC_FLOOD.

Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.")
Reported-at: https://github.com/ovn-org/ovn/issues/209

Signed-off-by: Daniel Ding <danieldin186@gmail.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
---
 northd/northd.c     | 18 +++++++++++++++++-
 tests/ovn-northd.at | 12 ++++++++++++
 2 files changed, 29 insertions(+), 1 deletion(-)

Comments

Dumitru Ceara Dec. 5, 2023, 10:58 a.m. UTC | #1
Hi Daniel,

Thanks for this new revision but why is it v3?  I don't think I saw v2
posted anywhere but maybe I missed it.

On 12/5/23 08:33, Daniel Ding wrote:
> If the router has a snat rule and it's external ip isn't lrp address,
> when the arp request from other router for this external ip, will
> be drop, because of this external ip use same mac address as lrp, so
> can not forward to MC_FLOOD.
> 
> Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.")
> Reported-at: https://github.com/ovn-org/ovn/issues/209
> 
> Signed-off-by: Daniel Ding <danieldin186@gmail.com>
> Acked-by: Dumitru Ceara <dceara@redhat.com>

Please don't add an "Acked-by: ... " if the person never explicitly
replied with "Acked-by: ... " on the previous version of the patch or
if you didn't have explicit agreement from that person to do so.

Quoting from my previous reply to your v1, I said:

"I think it makes sense to do what you're suggesting."

https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934

That doesn't mean I acked the patch.

> ---
>  northd/northd.c     | 18 +++++++++++++++++-
>  tests/ovn-northd.at | 12 ++++++++++++
>  2 files changed, 29 insertions(+), 1 deletion(-)
> 
> diff --git a/northd/northd.c b/northd/northd.c
> index e9cb906e2..99fb30f46 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
>          }
>      }
>  
> +    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
> +    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
> +
>      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>          const struct nbrec_nat *nat = nat_entry->nb;
> @@ -8983,7 +8986,17 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
>          }
>  
>          if (!strcmp(nat->type, "snat")) {
> -            continue;
> +            if (nat_entry_is_v6(nat_entry)) {
> +                if (sset_contains(&snat_ips_v6, nat->external_ip)) {
> +                    continue;
> +                }
> +                sset_add(&snat_ips_v6, nat->external_ip);
> +            } else {
> +                if (sset_contains(&snat_ips_v4, nat->external_ip)) {
> +                    continue;
> +                }
> +                sset_add(&snat_ips_v4, nat->external_ip);
> +            }

Essentially this just makes sure we don't skip NAT entries and that we
consider unique external_ips.  I'm fine with relaxing the second part of
the condition in which case, as mentioned on v1, I think we can just
remove the whole "if (!strcmp(nat->type, "snat")) {" block.

With the following incremental change applied (it removes the block)
your test still passes:

diff --git a/northd/northd.c b/northd/northd.c
index df7d2d60a5..20efd3b74c 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -9372,9 +9372,6 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
         }
     }
 
-    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
-    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
-
     for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
         struct ovn_nat *nat_entry = &op->od->nat_entries[i];
         const struct nbrec_nat *nat = nat_entry->nb;
@@ -9383,20 +9380,6 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
             continue;
         }
 
-        if (!strcmp(nat->type, "snat")) {
-            if (nat_entry_is_v6(nat_entry)) {
-                if (sset_contains(&snat_ips_v6, nat->external_ip)) {
-                    continue;
-                }
-                sset_add(&snat_ips_v6, nat->external_ip);
-            } else {
-                if (sset_contains(&snat_ips_v4, nat->external_ip)) {
-                    continue;
-                }
-                sset_add(&snat_ips_v4, nat->external_ip);
-            }
-        }
-
         /* Check if the ovn port has a network configured on which we could
          * expect ARP requests/NS for the DNAT external_ip.
          */
@@ -9436,9 +9419,6 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
     if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
         build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od, lflows);
     }
-
-    sset_destroy(&snat_ips_v4);
-    sset_destroy(&snat_ips_v6);
 }
 
 static void
---

Best regards,
Dumitru

>          }
>  
>          /* Check if the ovn port has a network configured on which we could
> @@ -9025,6 +9038,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
>      if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
>          build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od, lflows);
>      }
> +
> +    sset_destroy(&snat_ips_v4);
> +    sset_destroy(&snat_ips_v6);
>  }
>  
>  static void
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 5c9da811f..953e0d829 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -5084,6 +5084,7 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
>    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
>    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>  ])
> @@ -5098,6 +5099,7 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed 's/table=../table=??/' | sort],
>    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:02:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.2.1), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.100), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.200), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:201), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
>  ])
>  
> @@ -5118,8 +5120,10 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
>    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
>    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>  ])
>  
> @@ -5133,7 +5137,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed 's/table=../table=??/' | sort],
>    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:02:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.2.1), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.100), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.200), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 40.0.0.100), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 40.0.0.200), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:201), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
>  ])
>  
> @@ -5151,9 +5157,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
>    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
>    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>  ])
>  
> @@ -5169,9 +5177,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
>    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
>    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>  ])
>  
> @@ -5193,9 +5203,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
>    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
>    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
>  ])
>
Daniel Ding Dec. 5, 2023, 12:58 p.m. UTC | #2
On Tue, 5 Dec 2023 at 18:58, Dumitru Ceara <dceara@redhat.com> wrote:

> Hi Daniel,
>
> Thanks for this new revision but why is it v3?  I don't think I saw v2
> posted anywhere but maybe I missed it.
>
>
Sorry, that is my mistake.


> On 12/5/23 08:33, Daniel Ding wrote:
> > If the router has a snat rule and it's external ip isn't lrp address,
> > when the arp request from other router for this external ip, will
> > be drop, because of this external ip use same mac address as lrp, so
> > can not forward to MC_FLOOD.
> >
> > Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever
> possible.")
> > Reported-at: https://github.com/ovn-org/ovn/issues/209
> >
> > Signed-off-by: Daniel Ding <danieldin186@gmail.com>
> > Acked-by: Dumitru Ceara <dceara@redhat.com>
>
> Please don't add an "Acked-by: ... " if the person never explicitly
> replied with "Acked-by: ... " on the previous version of the patch or
> if you didn't have explicit agreement from that person to do so.
>
> Quoting from my previous reply to your v1, I said:
>
> "I think it makes sense to do what you're suggesting."
>
>
> https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934
>
> That doesn't mean I acked the patch.
>
>
Got it. Thx!


> > ---
> >  northd/northd.c     | 18 +++++++++++++++++-
> >  tests/ovn-northd.at | 12 ++++++++++++
> >  2 files changed, 29 insertions(+), 1 deletion(-)
> >
> > diff --git a/northd/northd.c b/northd/northd.c
> > index e9cb906e2..99fb30f46 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
> >          }
> >      }
> >
> > +    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
> > +    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
> > +
> >      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
> >          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
> >          const struct nbrec_nat *nat = nat_entry->nb;
> > @@ -8983,7 +8986,17 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
> >          }
> >
> >          if (!strcmp(nat->type, "snat")) {
> > -            continue;
> > +            if (nat_entry_is_v6(nat_entry)) {
> > +                if (sset_contains(&snat_ips_v6, nat->external_ip)) {
> > +                    continue;
> > +                }
> > +                sset_add(&snat_ips_v6, nat->external_ip);
> > +            } else {
> > +                if (sset_contains(&snat_ips_v4, nat->external_ip)) {
> > +                    continue;
> > +                }
> > +                sset_add(&snat_ips_v4, nat->external_ip);
> > +            }
>
> Essentially this just makes sure we don't skip NAT entries and that we
> consider unique external_ips.  I'm fine with relaxing the second part of
> the condition in which case, as mentioned on v1, I think we can just
> remove the whole "if (!strcmp(nat->type, "snat")) {" block.
>
> With the following incremental change applied (it removes the block)
> your test still passes:
>
> diff --git a/northd/northd.c b/northd/northd.c
> index df7d2d60a5..20efd3b74c 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -9372,9 +9372,6 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
>          }
>      }
>
> -    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
> -    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
> -
>      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>          const struct nbrec_nat *nat = nat_entry->nb;
> @@ -9383,20 +9380,6 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
>              continue;
>          }
>
> -        if (!strcmp(nat->type, "snat")) {
> -            if (nat_entry_is_v6(nat_entry)) {
> -                if (sset_contains(&snat_ips_v6, nat->external_ip)) {
> -                    continue;
> -                }
> -                sset_add(&snat_ips_v6, nat->external_ip);
> -            } else {
> -                if (sset_contains(&snat_ips_v4, nat->external_ip)) {
> -                    continue;
> -                }
> -                sset_add(&snat_ips_v4, nat->external_ip);
> -            }
> -        }
> -
>          /* Check if the ovn port has a network configured on which we
> could
>           * expect ARP requests/NS for the DNAT external_ip.
>           */
> @@ -9436,9 +9419,6 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
>      if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
>          build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od, lflows);
>      }
> -
> -    sset_destroy(&snat_ips_v4);
> -    sset_destroy(&snat_ips_v6);
>  }
>
>
If the nat_entries has multiple snats with the same external_ip, I think it
should check exernal_ip whether in a string hash. In addition, the
snat_and_dnat entry is working normally, so exclude it.

 static void
> ---
>
> Best regards,
> Dumitru
>
> >          }
> >
> >          /* Check if the ovn port has a network configured on which we
> could
> > @@ -9025,6 +9038,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
> >      if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
> >          build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
> lflows);
> >      }
> > +
> > +    sset_destroy(&snat_ips_v4);
> > +    sset_destroy(&snat_ips_v6);
> >  }
> >
> >  static void
> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > index 5c9da811f..953e0d829 100644
> > --- a/tests/ovn-northd.at
> > +++ b/tests/ovn-northd.at
> > @@ -5084,6 +5084,7 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed
> 's/table=../table=??/' | sort],
> >    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast),
> action=(outport = "_MC_flood"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src ==
> {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)),
> action=(outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >  ])
> > @@ -5098,6 +5099,7 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed
> 's/table=../table=??/' | sort],
> >    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src ==
> {00:00:00:00:02:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)),
> action=(outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.2.1), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 20.0.0.100), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 20.0.0.200), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && nd_ns && nd.target == fe80::200:ff:fe00:201), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> >  ])
> >
> > @@ -5118,8 +5120,10 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed
> 's/table=../table=??/' | sort],
> >    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast),
> action=(outport = "_MC_flood"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src ==
> {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)),
> action=(outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >  ])
> >
> > @@ -5133,7 +5137,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed
> 's/table=../table=??/' | sort],
> >    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src ==
> {00:00:00:00:02:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)),
> action=(outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.2.1), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 20.0.0.100), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 20.0.0.200), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 40.0.0.100), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 40.0.0.200), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && nd_ns && nd.target == fe80::200:ff:fe00:201), action=(clone {outport =
> "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
> >  ])
> >
> > @@ -5151,9 +5157,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed
> 's/table=../table=??/' | sort],
> >    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast),
> action=(outport = "_MC_flood"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src ==
> {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)),
> action=(outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >  ])
> >
> > @@ -5169,9 +5177,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed
> 's/table=../table=??/' | sort],
> >    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast),
> action=(outport = "_MC_flood"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src ==
> {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)),
> action=(outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >  ])
> >
> > @@ -5193,9 +5203,11 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed
> 's/table=../table=??/' | sort],
> >    table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast),
> action=(outport = "_MC_flood"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src ==
> {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)),
> action=(outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> > +  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >    table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0
> && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport =
> "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
> >  ])
> >
>
> Thank you, Dumitru Ceara!
Dumitru Ceara Dec. 5, 2023, 3:57 p.m. UTC | #3
On 12/5/23 13:58, Daniel Ding wrote:
> 
> 
> On Tue, 5 Dec 2023 at 18:58, Dumitru Ceara <dceara@redhat.com
> <mailto:dceara@redhat.com>> wrote:
> 
>     Hi Daniel,
> 
>     Thanks for this new revision but why is it v3?  I don't think I saw v2
>     posted anywhere but maybe I missed it.
> 
>  
> Sorry, that is my mistake.
>  

No problem.

> 
>     On 12/5/23 08:33, Daniel Ding wrote:
>     > If the router has a snat rule and it's external ip isn't lrp address,
>     > when the arp request from other router for this external ip, will
>     > be drop, because of this external ip use same mac address as lrp, so
>     > can not forward to MC_FLOOD.
>     >
>     > Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain
>     whenever possible.")
>     > Reported-at: https://github.com/ovn-org/ovn/issues/209
>     <https://github.com/ovn-org/ovn/issues/209>
>     >
>     > Signed-off-by: Daniel Ding <danieldin186@gmail.com
>     <mailto:danieldin186@gmail.com>>
>     > Acked-by: Dumitru Ceara <dceara@redhat.com <mailto:dceara@redhat.com>>
> 
>     Please don't add an "Acked-by: ... " if the person never explicitly
>     replied with "Acked-by: ... " on the previous version of the patch or
>     if you didn't have explicit agreement from that person to do so.
> 
>     Quoting from my previous reply to your v1, I said:
> 
>     "I think it makes sense to do what you're suggesting."
> 
>     https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934 <https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934>
> 
>     That doesn't mean I acked the patch.
> 
> 
> Got it. Thx!
>  

No worries.

> 
>     > ---
>     >  northd/northd.c     | 18 +++++++++++++++++-
>     >  tests/ovn-northd.at <http://ovn-northd.at> | 12 ++++++++++++
>     >  2 files changed, 29 insertions(+), 1 deletion(-)
>     >
>     > diff --git a/northd/northd.c b/northd/northd.c
>     > index e9cb906e2..99fb30f46 100644
>     > --- a/northd/northd.c
>     > +++ b/northd/northd.c
>     > @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct
>     ovn_port *op,
>     >          }
>     >      }
>     > 
>     > +    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
>     > +    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
>     > +
>     >      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>     >          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>     >          const struct nbrec_nat *nat = nat_entry->nb;
>     > @@ -8983,7 +8986,17 @@ build_lswitch_rport_arp_req_flows(struct
>     ovn_port *op,
>     >          }
>     > 
>     >          if (!strcmp(nat->type, "snat")) {
>     > -            continue;
>     > +            if (nat_entry_is_v6(nat_entry)) {
>     > +                if (sset_contains(&snat_ips_v6, nat->external_ip)) {
>     > +                    continue;
>     > +                }
>     > +                sset_add(&snat_ips_v6, nat->external_ip);
>     > +            } else {
>     > +                if (sset_contains(&snat_ips_v4, nat->external_ip)) {
>     > +                    continue;
>     > +                }
>     > +                sset_add(&snat_ips_v4, nat->external_ip);
>     > +            }
> 
>     Essentially this just makes sure we don't skip NAT entries and that we
>     consider unique external_ips.  I'm fine with relaxing the second part of
>     the condition in which case, as mentioned on v1, I think we can just
>     remove the whole "if (!strcmp(nat->type, "snat")) {" block.
> 
>     With the following incremental change applied (it removes the block)
>     your test still passes:
> 
>     diff --git a/northd/northd.c b/northd/northd.c
>     index df7d2d60a5..20efd3b74c 100644
>     --- a/northd/northd.c
>     +++ b/northd/northd.c
>     @@ -9372,9 +9372,6 @@ build_lswitch_rport_arp_req_flows(struct
>     ovn_port *op,
>              }
>          }
> 
>     -    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
>     -    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
>     -
>          for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>              struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>              const struct nbrec_nat *nat = nat_entry->nb;
>     @@ -9383,20 +9380,6 @@ build_lswitch_rport_arp_req_flows(struct
>     ovn_port *op,
>                  continue;
>              }
> 
>     -        if (!strcmp(nat->type, "snat")) {
>     -            if (nat_entry_is_v6(nat_entry)) {
>     -                if (sset_contains(&snat_ips_v6, nat->external_ip)) {
>     -                    continue;
>     -                }
>     -                sset_add(&snat_ips_v6, nat->external_ip);
>     -            } else {
>     -                if (sset_contains(&snat_ips_v4, nat->external_ip)) {
>     -                    continue;
>     -                }
>     -                sset_add(&snat_ips_v4, nat->external_ip);
>     -            }
>     -        }
>     -
>              /* Check if the ovn port has a network configured on which
>     we could
>               * expect ARP requests/NS for the DNAT external_ip.
>               */
>     @@ -9436,9 +9419,6 @@ build_lswitch_rport_arp_req_flows(struct
>     ovn_port *op,
>          if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
>              build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
>     lflows);
>          }
>     -
>     -    sset_destroy(&snat_ips_v4);
>     -    sset_destroy(&snat_ips_v6);
>      }
> 
> 
> If the nat_entries has multiple snats with the same external_ip, I think
> it should check exernal_ip whether in a string hash. In addition, the
> snat_and_dnat entry is working normally, so exclude it.
> 

I'm not sure I understand, we weren't skipping dnat_and_snat before and
we wouldn't be skipping it with this either.  Regarding the duplicates,
it really depends how common it is to have duplicates, I guess.

Regards,
Dumitru
Daniel Ding Dec. 6, 2023, 1:56 a.m. UTC | #4
Hi Dumitru!

On Tue, 5 Dec 2023 at 23:59, Dumitru Ceara <dceara@redhat.com> wrote:

> On 12/5/23 13:58, Daniel Ding wrote:
> >
> >
> > On Tue, 5 Dec 2023 at 18:58, Dumitru Ceara <dceara@redhat.com
> > <mailto:dceara@redhat.com>> wrote:
> >
> >     Hi Daniel,
> >
> >     Thanks for this new revision but why is it v3?  I don't think I saw
> v2
> >     posted anywhere but maybe I missed it.
> >
> >
> > Sorry, that is my mistake.
> >
>
> No problem.
>
> >
> >     On 12/5/23 08:33, Daniel Ding wrote:
> >     > If the router has a snat rule and it's external ip isn't lrp
> address,
> >     > when the arp request from other router for this external ip, will
> >     > be drop, because of this external ip use same mac address as lrp,
> so
> >     > can not forward to MC_FLOOD.
> >     >
> >     > Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain
> >     whenever possible.")
> >     > Reported-at: https://github.com/ovn-org/ovn/issues/209
> >     <https://github.com/ovn-org/ovn/issues/209>
> >     >
> >     > Signed-off-by: Daniel Ding <danieldin186@gmail.com
> >     <mailto:danieldin186@gmail.com>>
> >     > Acked-by: Dumitru Ceara <dceara@redhat.com <mailto:
> dceara@redhat.com>>
> >
> >     Please don't add an "Acked-by: ... " if the person never explicitly
> >     replied with "Acked-by: ... " on the previous version of the patch or
> >     if you didn't have explicit agreement from that person to do so.
> >
> >     Quoting from my previous reply to your v1, I said:
> >
> >     "I think it makes sense to do what you're suggesting."
> >
> >
> https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934
> <
> https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934
> >
> >
> >     That doesn't mean I acked the patch.
> >
> >
> > Got it. Thx!
> >
>
> No worries.
>
> >
> >     > ---
> >     >  northd/northd.c     | 18 +++++++++++++++++-
> >     >  tests/ovn-northd.at <http://ovn-northd.at> | 12 ++++++++++++
> >     >  2 files changed, 29 insertions(+), 1 deletion(-)
> >     >
> >     > diff --git a/northd/northd.c b/northd/northd.c
> >     > index e9cb906e2..99fb30f46 100644
> >     > --- a/northd/northd.c
> >     > +++ b/northd/northd.c
> >     > @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct
> >     ovn_port *op,
> >     >          }
> >     >      }
> >     >
> >     > +    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
> >     > +    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
> >     > +
> >     >      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
> >     >          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
> >     >          const struct nbrec_nat *nat = nat_entry->nb;
> >     > @@ -8983,7 +8986,17 @@ build_lswitch_rport_arp_req_flows(struct
> >     ovn_port *op,
> >     >          }
> >     >
> >     >          if (!strcmp(nat->type, "snat")) {
> >     > -            continue;
> >     > +            if (nat_entry_is_v6(nat_entry)) {
> >     > +                if (sset_contains(&snat_ips_v6,
> nat->external_ip)) {
> >     > +                    continue;
> >     > +                }
> >     > +                sset_add(&snat_ips_v6, nat->external_ip);
> >     > +            } else {
> >     > +                if (sset_contains(&snat_ips_v4,
> nat->external_ip)) {
> >     > +                    continue;
> >     > +                }
> >     > +                sset_add(&snat_ips_v4, nat->external_ip);
> >     > +            }
> >
> >     Essentially this just makes sure we don't skip NAT entries and that
> we
> >     consider unique external_ips.  I'm fine with relaxing the second
> part of
> >     the condition in which case, as mentioned on v1, I think we can just
> >     remove the whole "if (!strcmp(nat->type, "snat")) {" block.
> >
> >     With the following incremental change applied (it removes the block)
> >     your test still passes:
> >
> >     diff --git a/northd/northd.c b/northd/northd.c
> >     index df7d2d60a5..20efd3b74c 100644
> >     --- a/northd/northd.c
> >     +++ b/northd/northd.c
> >     @@ -9372,9 +9372,6 @@ build_lswitch_rport_arp_req_flows(struct
> >     ovn_port *op,
> >              }
> >          }
> >
> >     -    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
> >     -    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
> >     -
> >          for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
> >              struct ovn_nat *nat_entry = &op->od->nat_entries[i];
> >              const struct nbrec_nat *nat = nat_entry->nb;
> >     @@ -9383,20 +9380,6 @@ build_lswitch_rport_arp_req_flows(struct
> >     ovn_port *op,
> >                  continue;
> >              }
> >
> >     -        if (!strcmp(nat->type, "snat")) {
> >     -            if (nat_entry_is_v6(nat_entry)) {
> >     -                if (sset_contains(&snat_ips_v6, nat->external_ip)) {
> >     -                    continue;
> >     -                }
> >     -                sset_add(&snat_ips_v6, nat->external_ip);
> >     -            } else {
> >     -                if (sset_contains(&snat_ips_v4, nat->external_ip)) {
> >     -                    continue;
> >     -                }
> >     -                sset_add(&snat_ips_v4, nat->external_ip);
> >     -            }
> >     -        }
> >     -
> >              /* Check if the ovn port has a network configured on which
> >     we could
> >               * expect ARP requests/NS for the DNAT external_ip.
> >               */
> >     @@ -9436,9 +9419,6 @@ build_lswitch_rport_arp_req_flows(struct
> >     ovn_port *op,
> >          if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
> >              build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
> >     lflows);
> >          }
> >     -
> >     -    sset_destroy(&snat_ips_v4);
> >     -    sset_destroy(&snat_ips_v6);
> >      }
> >
> >
> > If the nat_entries has multiple snats with the same external_ip, I think
> > it should check exernal_ip whether in a string hash. In addition, the
> > snat_and_dnat entry is working normally, so exclude it.
> >
>
> I'm not sure I understand, we weren't skipping dnat_and_snat before and
> we wouldn't be skipping it with this either.  Regarding the duplicates,
> it really depends how common it is to have duplicates, I guess.
>
>
If I understand correctly, we just remove "if (!strcmp(nat->type, "snat"))
{" block. And keep external checking because we are not sure about too many
snats in a lr.

diff --git a/northd/northd.c b/northd/northd.c
index e9cb906e2..d93870e25 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
         }
     }

+    struct sset nat_ips_v4 = SSET_INITIALIZER(&nat_ips_v4);
+    struct sset nat_ips_v6 = SSET_INITIALIZER(&nat_ips_v6);
+
     for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
         struct ovn_nat *nat_entry = &op->od->nat_entries[i];
         const struct nbrec_nat *nat = nat_entry->nb;
@@ -8982,8 +8985,16 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
*op,
             continue;
         }

-        if (!strcmp(nat->type, "snat")) {
-            continue;
+        if (nat_entry_is_v6(nat_entry)) {
+            if (sset_contains(&nat_ips_v6, nat->external_ip)) {
+                continue;
+            }
+            sset_add(&nat_ips_v6, nat->external_ip);
+        } else {
+            if (sset_contains(&nat_ips_v4, nat->external_ip)) {
+                continue;
+            }
+            sset_add(&nat_ips_v4, nat->external_ip);
         }

         /* Check if the ovn port has a network configured on which we could
@@ -9025,6 +9036,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
     if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
         build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od, lflows);
     }
+
+    sset_destroy(&nat_ips_v4);
+    sset_destroy(&nat_ips_v6);
 }

Regards,
> Dumitru
>
>
Dumitru Ceara Dec. 7, 2023, 1:26 p.m. UTC | #5
On 12/6/23 02:56, Daniel Ding wrote:
> Hi Dumitru!
> 
> On Tue, 5 Dec 2023 at 23:59, Dumitru Ceara <dceara@redhat.com> wrote:
> 
>> On 12/5/23 13:58, Daniel Ding wrote:
>>>
>>>
>>> On Tue, 5 Dec 2023 at 18:58, Dumitru Ceara <dceara@redhat.com
>>> <mailto:dceara@redhat.com>> wrote:
>>>
>>>     Hi Daniel,
>>>
>>>     Thanks for this new revision but why is it v3?  I don't think I saw
>> v2
>>>     posted anywhere but maybe I missed it.
>>>
>>>
>>> Sorry, that is my mistake.
>>>
>>
>> No problem.
>>
>>>
>>>     On 12/5/23 08:33, Daniel Ding wrote:
>>>     > If the router has a snat rule and it's external ip isn't lrp
>> address,
>>>     > when the arp request from other router for this external ip, will
>>>     > be drop, because of this external ip use same mac address as lrp,
>> so
>>>     > can not forward to MC_FLOOD.
>>>     >
>>>     > Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain
>>>     whenever possible.")
>>>     > Reported-at: https://github.com/ovn-org/ovn/issues/209
>>>     <https://github.com/ovn-org/ovn/issues/209>
>>>     >
>>>     > Signed-off-by: Daniel Ding <danieldin186@gmail.com
>>>     <mailto:danieldin186@gmail.com>>
>>>     > Acked-by: Dumitru Ceara <dceara@redhat.com <mailto:
>> dceara@redhat.com>>
>>>
>>>     Please don't add an "Acked-by: ... " if the person never explicitly
>>>     replied with "Acked-by: ... " on the previous version of the patch or
>>>     if you didn't have explicit agreement from that person to do so.
>>>
>>>     Quoting from my previous reply to your v1, I said:
>>>
>>>     "I think it makes sense to do what you're suggesting."
>>>
>>>
>> https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934
>> <
>> https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934
>>>
>>>
>>>     That doesn't mean I acked the patch.
>>>
>>>
>>> Got it. Thx!
>>>
>>
>> No worries.
>>
>>>
>>>     > ---
>>>     >  northd/northd.c     | 18 +++++++++++++++++-
>>>     >  tests/ovn-northd.at <http://ovn-northd.at> | 12 ++++++++++++
>>>     >  2 files changed, 29 insertions(+), 1 deletion(-)
>>>     >
>>>     > diff --git a/northd/northd.c b/northd/northd.c
>>>     > index e9cb906e2..99fb30f46 100644
>>>     > --- a/northd/northd.c
>>>     > +++ b/northd/northd.c
>>>     > @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct
>>>     ovn_port *op,
>>>     >          }
>>>     >      }
>>>     >
>>>     > +    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
>>>     > +    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
>>>     > +
>>>     >      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>>>     >          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>>>     >          const struct nbrec_nat *nat = nat_entry->nb;
>>>     > @@ -8983,7 +8986,17 @@ build_lswitch_rport_arp_req_flows(struct
>>>     ovn_port *op,
>>>     >          }
>>>     >
>>>     >          if (!strcmp(nat->type, "snat")) {
>>>     > -            continue;
>>>     > +            if (nat_entry_is_v6(nat_entry)) {
>>>     > +                if (sset_contains(&snat_ips_v6,
>> nat->external_ip)) {
>>>     > +                    continue;
>>>     > +                }
>>>     > +                sset_add(&snat_ips_v6, nat->external_ip);
>>>     > +            } else {
>>>     > +                if (sset_contains(&snat_ips_v4,
>> nat->external_ip)) {
>>>     > +                    continue;
>>>     > +                }
>>>     > +                sset_add(&snat_ips_v4, nat->external_ip);
>>>     > +            }
>>>
>>>     Essentially this just makes sure we don't skip NAT entries and that
>> we
>>>     consider unique external_ips.  I'm fine with relaxing the second
>> part of
>>>     the condition in which case, as mentioned on v1, I think we can just
>>>     remove the whole "if (!strcmp(nat->type, "snat")) {" block.
>>>
>>>     With the following incremental change applied (it removes the block)
>>>     your test still passes:
>>>
>>>     diff --git a/northd/northd.c b/northd/northd.c
>>>     index df7d2d60a5..20efd3b74c 100644
>>>     --- a/northd/northd.c
>>>     +++ b/northd/northd.c
>>>     @@ -9372,9 +9372,6 @@ build_lswitch_rport_arp_req_flows(struct
>>>     ovn_port *op,
>>>              }
>>>          }
>>>
>>>     -    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
>>>     -    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
>>>     -
>>>          for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>>>              struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>>>              const struct nbrec_nat *nat = nat_entry->nb;
>>>     @@ -9383,20 +9380,6 @@ build_lswitch_rport_arp_req_flows(struct
>>>     ovn_port *op,
>>>                  continue;
>>>              }
>>>
>>>     -        if (!strcmp(nat->type, "snat")) {
>>>     -            if (nat_entry_is_v6(nat_entry)) {
>>>     -                if (sset_contains(&snat_ips_v6, nat->external_ip)) {
>>>     -                    continue;
>>>     -                }
>>>     -                sset_add(&snat_ips_v6, nat->external_ip);
>>>     -            } else {
>>>     -                if (sset_contains(&snat_ips_v4, nat->external_ip)) {
>>>     -                    continue;
>>>     -                }
>>>     -                sset_add(&snat_ips_v4, nat->external_ip);
>>>     -            }
>>>     -        }
>>>     -
>>>              /* Check if the ovn port has a network configured on which
>>>     we could
>>>               * expect ARP requests/NS for the DNAT external_ip.
>>>               */
>>>     @@ -9436,9 +9419,6 @@ build_lswitch_rport_arp_req_flows(struct
>>>     ovn_port *op,
>>>          if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
>>>              build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
>>>     lflows);
>>>          }
>>>     -
>>>     -    sset_destroy(&snat_ips_v4);
>>>     -    sset_destroy(&snat_ips_v6);
>>>      }
>>>
>>>
>>> If the nat_entries has multiple snats with the same external_ip, I think
>>> it should check exernal_ip whether in a string hash. In addition, the
>>> snat_and_dnat entry is working normally, so exclude it.
>>>
>>
>> I'm not sure I understand, we weren't skipping dnat_and_snat before and
>> we wouldn't be skipping it with this either.  Regarding the duplicates,
>> it really depends how common it is to have duplicates, I guess.
>>
>>
> If I understand correctly, we just remove "if (!strcmp(nat->type, "snat"))
> {" block. And keep external checking because we are not sure about too many
> snats in a lr.

If we're worried about duplicate SNAT IPs, we should probably just use
the precomputed op->od->snat_ips shash.  It already consists only of
unique external IPs used for SNAT on the router.

So no need for the additional ssets you're using.  We can just walk the
elements of the "nat_ips" shash directly.

> 
> diff --git a/northd/northd.c b/northd/northd.c
> index e9cb906e2..d93870e25 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
>          }
>      }
> 
> +    struct sset nat_ips_v4 = SSET_INITIALIZER(&nat_ips_v4);
> +    struct sset nat_ips_v6 = SSET_INITIALIZER(&nat_ips_v6);
> +
>      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>          const struct nbrec_nat *nat = nat_entry->nb;
> @@ -8982,8 +8985,16 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
>              continue;
>          }
> 
> -        if (!strcmp(nat->type, "snat")) {
> -            continue;
> +        if (nat_entry_is_v6(nat_entry)) {
> +            if (sset_contains(&nat_ips_v6, nat->external_ip)) {
> +                continue;
> +            }
> +            sset_add(&nat_ips_v6, nat->external_ip);
> +        } else {
> +            if (sset_contains(&nat_ips_v4, nat->external_ip)) {
> +                continue;
> +            }
> +            sset_add(&nat_ips_v4, nat->external_ip);
>          }
> 
>          /* Check if the ovn port has a network configured on which we could
> @@ -9025,6 +9036,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
>      if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
>          build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od, lflows);
>      }
> +
> +    sset_destroy(&nat_ips_v4);
> +    sset_destroy(&nat_ips_v6);
>  }
> 

Regards,
Dumitru
Daniel Ding Dec. 8, 2023, 1:51 p.m. UTC | #6
On Thu, 7 Dec 2023 at 21:26, Dumitru Ceara <dceara@redhat.com> wrote:

> On 12/6/23 02:56, Daniel Ding wrote:
> > Hi Dumitru!
> >
> > On Tue, 5 Dec 2023 at 23:59, Dumitru Ceara <dceara@redhat.com> wrote:
> >
> >> On 12/5/23 13:58, Daniel Ding wrote:
> >>>
> >>>
> >>> On Tue, 5 Dec 2023 at 18:58, Dumitru Ceara <dceara@redhat.com
> >>> <mailto:dceara@redhat.com>> wrote:
> >>>
> >>>     Hi Daniel,
> >>>
> >>>     Thanks for this new revision but why is it v3?  I don't think I saw
> >> v2
> >>>     posted anywhere but maybe I missed it.
> >>>
> >>>
> >>> Sorry, that is my mistake.
> >>>
> >>
> >> No problem.
> >>
> >>>
> >>>     On 12/5/23 08:33, Daniel Ding wrote:
> >>>     > If the router has a snat rule and it's external ip isn't lrp
> >> address,
> >>>     > when the arp request from other router for this external ip, will
> >>>     > be drop, because of this external ip use same mac address as lrp,
> >> so
> >>>     > can not forward to MC_FLOOD.
> >>>     >
> >>>     > Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain
> >>>     whenever possible.")
> >>>     > Reported-at: https://github.com/ovn-org/ovn/issues/209
> >>>     <https://github.com/ovn-org/ovn/issues/209>
> >>>     >
> >>>     > Signed-off-by: Daniel Ding <danieldin186@gmail.com
> >>>     <mailto:danieldin186@gmail.com>>
> >>>     > Acked-by: Dumitru Ceara <dceara@redhat.com <mailto:
> >> dceara@redhat.com>>
> >>>
> >>>     Please don't add an "Acked-by: ... " if the person never explicitly
> >>>     replied with "Acked-by: ... " on the previous version of the patch
> or
> >>>     if you didn't have explicit agreement from that person to do so.
> >>>
> >>>     Quoting from my previous reply to your v1, I said:
> >>>
> >>>     "I think it makes sense to do what you're suggesting."
> >>>
> >>>
> >>
> https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934
> >> <
> >>
> https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934
> >>>
> >>>
> >>>     That doesn't mean I acked the patch.
> >>>
> >>>
> >>> Got it. Thx!
> >>>
> >>
> >> No worries.
> >>
> >>>
> >>>     > ---
> >>>     >  northd/northd.c     | 18 +++++++++++++++++-
> >>>     >  tests/ovn-northd.at <http://ovn-northd.at> | 12 ++++++++++++
> >>>     >  2 files changed, 29 insertions(+), 1 deletion(-)
> >>>     >
> >>>     > diff --git a/northd/northd.c b/northd/northd.c
> >>>     > index e9cb906e2..99fb30f46 100644
> >>>     > --- a/northd/northd.c
> >>>     > +++ b/northd/northd.c
> >>>     > @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct
> >>>     ovn_port *op,
> >>>     >          }
> >>>     >      }
> >>>     >
> >>>     > +    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
> >>>     > +    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
> >>>     > +
> >>>     >      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
> >>>     >          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
> >>>     >          const struct nbrec_nat *nat = nat_entry->nb;
> >>>     > @@ -8983,7 +8986,17 @@ build_lswitch_rport_arp_req_flows(struct
> >>>     ovn_port *op,
> >>>     >          }
> >>>     >
> >>>     >          if (!strcmp(nat->type, "snat")) {
> >>>     > -            continue;
> >>>     > +            if (nat_entry_is_v6(nat_entry)) {
> >>>     > +                if (sset_contains(&snat_ips_v6,
> >> nat->external_ip)) {
> >>>     > +                    continue;
> >>>     > +                }
> >>>     > +                sset_add(&snat_ips_v6, nat->external_ip);
> >>>     > +            } else {
> >>>     > +                if (sset_contains(&snat_ips_v4,
> >> nat->external_ip)) {
> >>>     > +                    continue;
> >>>     > +                }
> >>>     > +                sset_add(&snat_ips_v4, nat->external_ip);
> >>>     > +            }
> >>>
> >>>     Essentially this just makes sure we don't skip NAT entries and that
> >> we
> >>>     consider unique external_ips.  I'm fine with relaxing the second
> >> part of
> >>>     the condition in which case, as mentioned on v1, I think we can
> just
> >>>     remove the whole "if (!strcmp(nat->type, "snat")) {" block.
> >>>
> >>>     With the following incremental change applied (it removes the
> block)
> >>>     your test still passes:
> >>>
> >>>     diff --git a/northd/northd.c b/northd/northd.c
> >>>     index df7d2d60a5..20efd3b74c 100644
> >>>     --- a/northd/northd.c
> >>>     +++ b/northd/northd.c
> >>>     @@ -9372,9 +9372,6 @@ build_lswitch_rport_arp_req_flows(struct
> >>>     ovn_port *op,
> >>>              }
> >>>          }
> >>>
> >>>     -    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
> >>>     -    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
> >>>     -
> >>>          for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
> >>>              struct ovn_nat *nat_entry = &op->od->nat_entries[i];
> >>>              const struct nbrec_nat *nat = nat_entry->nb;
> >>>     @@ -9383,20 +9380,6 @@ build_lswitch_rport_arp_req_flows(struct
> >>>     ovn_port *op,
> >>>                  continue;
> >>>              }
> >>>
> >>>     -        if (!strcmp(nat->type, "snat")) {
> >>>     -            if (nat_entry_is_v6(nat_entry)) {
> >>>     -                if (sset_contains(&snat_ips_v6,
> nat->external_ip)) {
> >>>     -                    continue;
> >>>     -                }
> >>>     -                sset_add(&snat_ips_v6, nat->external_ip);
> >>>     -            } else {
> >>>     -                if (sset_contains(&snat_ips_v4,
> nat->external_ip)) {
> >>>     -                    continue;
> >>>     -                }
> >>>     -                sset_add(&snat_ips_v4, nat->external_ip);
> >>>     -            }
> >>>     -        }
> >>>     -
> >>>              /* Check if the ovn port has a network configured on which
> >>>     we could
> >>>               * expect ARP requests/NS for the DNAT external_ip.
> >>>               */
> >>>     @@ -9436,9 +9419,6 @@ build_lswitch_rport_arp_req_flows(struct
> >>>     ovn_port *op,
> >>>          if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
> >>>              build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
> >>>     lflows);
> >>>          }
> >>>     -
> >>>     -    sset_destroy(&snat_ips_v4);
> >>>     -    sset_destroy(&snat_ips_v6);
> >>>      }
> >>>
> >>>
> >>> If the nat_entries has multiple snats with the same external_ip, I
> think
> >>> it should check exernal_ip whether in a string hash. In addition, the
> >>> snat_and_dnat entry is working normally, so exclude it.
> >>>
> >>
> >> I'm not sure I understand, we weren't skipping dnat_and_snat before and
> >> we wouldn't be skipping it with this either.  Regarding the duplicates,
> >> it really depends how common it is to have duplicates, I guess.
> >>
> >>
> > If I understand correctly, we just remove "if (!strcmp(nat->type,
> "snat"))
> > {" block. And keep external checking because we are not sure about too
> many
> > snats in a lr.
>
> If we're worried about duplicate SNAT IPs, we should probably just use
> the precomputed op->od->snat_ips shash.  It already consists only of
> unique external IPs used for SNAT on the router.
>
> So no need for the additional ssets you're using.  We can just walk the
> elements of the "nat_ips" shash directly.
>

Hi, Dumitru! I'm using od->snat_ips shash to ensure unique external IPs for
SNAT. And please help to review again. Thank you!

diff --git a/northd/northd.c b/northd/northd.c
index 65328434a..08b09da02 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -7170,6 +7170,37 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
*op,
         }
     }

+    struct shash_node *snat_snode;
+    SHASH_FOR_EACH (snat_snode, &op->od->snat_ips) {
+        struct ovn_snat_ip *snat_ip = snat_snode->data;
+
+        if (ovs_list_is_empty(&snat_ip->snat_entries)) {
+            continue;
+        }
+
+        struct ovn_nat *nat_entry =
+            CONTAINER_OF(ovs_list_front(&snat_ip->snat_entries),
+                         struct ovn_nat, ext_addr_list_node);
+        const struct nbrec_nat *nat = nat_entry->nb;
+
+        /* Check if the ovn port has a network configured on which we could
+         * expect ARP requests/NS for the DNAT external_ip.
+         */
+        if (nat_entry_is_v6(nat_entry)) {
+            if (!sset_contains(&op->od->lb_ips_v6, nat->external_ip)) {
+                build_lswitch_rport_arp_req_flow(
+                    nat->external_ip, AF_INET6, sw_op, sw_od, 80, lflows,
+                    stage_hint);
+            }
+        } else {
+            if (!sset_contains(&op->od->lb_ips_v4, nat->external_ip)) {
+                build_lswitch_rport_arp_req_flow(
+                    nat->external_ip, AF_INET, sw_op, sw_od, 80, lflows,
+                    stage_hint);
+            }
+        }
+    }
+
     for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
         build_lswitch_rport_arp_req_flow(
             op->lrp_networks.ipv4_addrs[i].addr_s, AF_INET, sw_op, sw_od,
80,


>
> >
> > diff --git a/northd/northd.c b/northd/northd.c
> > index e9cb906e2..d93870e25 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
> >          }
> >      }
> >
> > +    struct sset nat_ips_v4 = SSET_INITIALIZER(&nat_ips_v4);
> > +    struct sset nat_ips_v6 = SSET_INITIALIZER(&nat_ips_v6);
> > +
> >      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
> >          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
> >          const struct nbrec_nat *nat = nat_entry->nb;
> > @@ -8982,8 +8985,16 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> > *op,
> >              continue;
> >          }
> >
> > -        if (!strcmp(nat->type, "snat")) {
> > -            continue;
> > +        if (nat_entry_is_v6(nat_entry)) {
> > +            if (sset_contains(&nat_ips_v6, nat->external_ip)) {
> > +                continue;
> > +            }
> > +            sset_add(&nat_ips_v6, nat->external_ip);
> > +        } else {
> > +            if (sset_contains(&nat_ips_v4, nat->external_ip)) {
> > +                continue;
> > +            }
> > +            sset_add(&nat_ips_v4, nat->external_ip);
> >          }
> >
> >          /* Check if the ovn port has a network configured on which we
> could
> > @@ -9025,6 +9036,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
> >      if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
> >          build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
> lflows);
> >      }
> > +
> > +    sset_destroy(&nat_ips_v4);
> > +    sset_destroy(&nat_ips_v6);
> >  }
> >
>
> Regards,
> Dumitru
>
>
Dumitru Ceara Dec. 8, 2023, 2:43 p.m. UTC | #7
On 12/8/23 14:51, Daniel Ding wrote:
> On Thu, 7 Dec 2023 at 21:26, Dumitru Ceara <dceara@redhat.com> wrote:
> 
>> On 12/6/23 02:56, Daniel Ding wrote:
>>> Hi Dumitru!
>>>
>>> On Tue, 5 Dec 2023 at 23:59, Dumitru Ceara <dceara@redhat.com> wrote:
>>>
>>>> On 12/5/23 13:58, Daniel Ding wrote:
>>>>>
>>>>>
>>>>> On Tue, 5 Dec 2023 at 18:58, Dumitru Ceara <dceara@redhat.com
>>>>> <mailto:dceara@redhat.com>> wrote:
>>>>>
>>>>>     Hi Daniel,
>>>>>
>>>>>     Thanks for this new revision but why is it v3?  I don't think I saw
>>>> v2
>>>>>     posted anywhere but maybe I missed it.
>>>>>
>>>>>
>>>>> Sorry, that is my mistake.
>>>>>
>>>>
>>>> No problem.
>>>>
>>>>>
>>>>>     On 12/5/23 08:33, Daniel Ding wrote:
>>>>>     > If the router has a snat rule and it's external ip isn't lrp
>>>> address,
>>>>>     > when the arp request from other router for this external ip, will
>>>>>     > be drop, because of this external ip use same mac address as lrp,
>>>> so
>>>>>     > can not forward to MC_FLOOD.
>>>>>     >
>>>>>     > Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain
>>>>>     whenever possible.")
>>>>>     > Reported-at: https://github.com/ovn-org/ovn/issues/209
>>>>>     <https://github.com/ovn-org/ovn/issues/209>
>>>>>     >
>>>>>     > Signed-off-by: Daniel Ding <danieldin186@gmail.com
>>>>>     <mailto:danieldin186@gmail.com>>
>>>>>     > Acked-by: Dumitru Ceara <dceara@redhat.com <mailto:
>>>> dceara@redhat.com>>
>>>>>
>>>>>     Please don't add an "Acked-by: ... " if the person never explicitly
>>>>>     replied with "Acked-by: ... " on the previous version of the patch
>> or
>>>>>     if you didn't have explicit agreement from that person to do so.
>>>>>
>>>>>     Quoting from my previous reply to your v1, I said:
>>>>>
>>>>>     "I think it makes sense to do what you're suggesting."
>>>>>
>>>>>
>>>>
>> https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934
>>>> <
>>>>
>> https://patchwork.ozlabs.org/project/ovn/patch/CALLnitDhFcEtzAPKHJtddyX1CX_2cYS2OtqScrhpv6Jse0MV6g@mail.gmail.com/#3214934
>>>>>
>>>>>
>>>>>     That doesn't mean I acked the patch.
>>>>>
>>>>>
>>>>> Got it. Thx!
>>>>>
>>>>
>>>> No worries.
>>>>
>>>>>
>>>>>     > ---
>>>>>     >  northd/northd.c     | 18 +++++++++++++++++-
>>>>>     >  tests/ovn-northd.at <http://ovn-northd.at> | 12 ++++++++++++
>>>>>     >  2 files changed, 29 insertions(+), 1 deletion(-)
>>>>>     >
>>>>>     > diff --git a/northd/northd.c b/northd/northd.c
>>>>>     > index e9cb906e2..99fb30f46 100644
>>>>>     > --- a/northd/northd.c
>>>>>     > +++ b/northd/northd.c
>>>>>     > @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct
>>>>>     ovn_port *op,
>>>>>     >          }
>>>>>     >      }
>>>>>     >
>>>>>     > +    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
>>>>>     > +    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
>>>>>     > +
>>>>>     >      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>>>>>     >          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>>>>>     >          const struct nbrec_nat *nat = nat_entry->nb;
>>>>>     > @@ -8983,7 +8986,17 @@ build_lswitch_rport_arp_req_flows(struct
>>>>>     ovn_port *op,
>>>>>     >          }
>>>>>     >
>>>>>     >          if (!strcmp(nat->type, "snat")) {
>>>>>     > -            continue;
>>>>>     > +            if (nat_entry_is_v6(nat_entry)) {
>>>>>     > +                if (sset_contains(&snat_ips_v6,
>>>> nat->external_ip)) {
>>>>>     > +                    continue;
>>>>>     > +                }
>>>>>     > +                sset_add(&snat_ips_v6, nat->external_ip);
>>>>>     > +            } else {
>>>>>     > +                if (sset_contains(&snat_ips_v4,
>>>> nat->external_ip)) {
>>>>>     > +                    continue;
>>>>>     > +                }
>>>>>     > +                sset_add(&snat_ips_v4, nat->external_ip);
>>>>>     > +            }
>>>>>
>>>>>     Essentially this just makes sure we don't skip NAT entries and that
>>>> we
>>>>>     consider unique external_ips.  I'm fine with relaxing the second
>>>> part of
>>>>>     the condition in which case, as mentioned on v1, I think we can
>> just
>>>>>     remove the whole "if (!strcmp(nat->type, "snat")) {" block.
>>>>>
>>>>>     With the following incremental change applied (it removes the
>> block)
>>>>>     your test still passes:
>>>>>
>>>>>     diff --git a/northd/northd.c b/northd/northd.c
>>>>>     index df7d2d60a5..20efd3b74c 100644
>>>>>     --- a/northd/northd.c
>>>>>     +++ b/northd/northd.c
>>>>>     @@ -9372,9 +9372,6 @@ build_lswitch_rport_arp_req_flows(struct
>>>>>     ovn_port *op,
>>>>>              }
>>>>>          }
>>>>>
>>>>>     -    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
>>>>>     -    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
>>>>>     -
>>>>>          for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>>>>>              struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>>>>>              const struct nbrec_nat *nat = nat_entry->nb;
>>>>>     @@ -9383,20 +9380,6 @@ build_lswitch_rport_arp_req_flows(struct
>>>>>     ovn_port *op,
>>>>>                  continue;
>>>>>              }
>>>>>
>>>>>     -        if (!strcmp(nat->type, "snat")) {
>>>>>     -            if (nat_entry_is_v6(nat_entry)) {
>>>>>     -                if (sset_contains(&snat_ips_v6,
>> nat->external_ip)) {
>>>>>     -                    continue;
>>>>>     -                }
>>>>>     -                sset_add(&snat_ips_v6, nat->external_ip);
>>>>>     -            } else {
>>>>>     -                if (sset_contains(&snat_ips_v4,
>> nat->external_ip)) {
>>>>>     -                    continue;
>>>>>     -                }
>>>>>     -                sset_add(&snat_ips_v4, nat->external_ip);
>>>>>     -            }
>>>>>     -        }
>>>>>     -
>>>>>              /* Check if the ovn port has a network configured on which
>>>>>     we could
>>>>>               * expect ARP requests/NS for the DNAT external_ip.
>>>>>               */
>>>>>     @@ -9436,9 +9419,6 @@ build_lswitch_rport_arp_req_flows(struct
>>>>>     ovn_port *op,
>>>>>          if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
>>>>>              build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
>>>>>     lflows);
>>>>>          }
>>>>>     -
>>>>>     -    sset_destroy(&snat_ips_v4);
>>>>>     -    sset_destroy(&snat_ips_v6);
>>>>>      }
>>>>>
>>>>>
>>>>> If the nat_entries has multiple snats with the same external_ip, I
>> think
>>>>> it should check exernal_ip whether in a string hash. In addition, the
>>>>> snat_and_dnat entry is working normally, so exclude it.
>>>>>
>>>>
>>>> I'm not sure I understand, we weren't skipping dnat_and_snat before and
>>>> we wouldn't be skipping it with this either.  Regarding the duplicates,
>>>> it really depends how common it is to have duplicates, I guess.
>>>>
>>>>
>>> If I understand correctly, we just remove "if (!strcmp(nat->type,
>> "snat"))
>>> {" block. And keep external checking because we are not sure about too
>> many
>>> snats in a lr.
>>
>> If we're worried about duplicate SNAT IPs, we should probably just use
>> the precomputed op->od->snat_ips shash.  It already consists only of
>> unique external IPs used for SNAT on the router.
>>
>> So no need for the additional ssets you're using.  We can just walk the
>> elements of the "nat_ips" shash directly.
>>
> 
> Hi, Dumitru! I'm using od->snat_ips shash to ensure unique external IPs for
> SNAT. And please help to review again. Thank you!
> 
> diff --git a/northd/northd.c b/northd/northd.c
> index 65328434a..08b09da02 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -7170,6 +7170,37 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
> *op,
>          }
>      }
> 
> +    struct shash_node *snat_snode;
> +    SHASH_FOR_EACH (snat_snode, &op->od->snat_ips) {
> +        struct ovn_snat_ip *snat_ip = snat_snode->data;
> +
> +        if (ovs_list_is_empty(&snat_ip->snat_entries)) {
> +            continue;
> +        }
> +
> +        struct ovn_nat *nat_entry =
> +            CONTAINER_OF(ovs_list_front(&snat_ip->snat_entries),
> +                         struct ovn_nat, ext_addr_list_node);
> +        const struct nbrec_nat *nat = nat_entry->nb;
> +
> +        /* Check if the ovn port has a network configured on which we could
> +         * expect ARP requests/NS for the DNAT external_ip.
> +         */
> +        if (nat_entry_is_v6(nat_entry)) {
> +            if (!sset_contains(&op->od->lb_ips_v6, nat->external_ip)) {
> +                build_lswitch_rport_arp_req_flow(
> +                    nat->external_ip, AF_INET6, sw_op, sw_od, 80, lflows,
> +                    stage_hint);
> +            }
> +        } else {
> +            if (!sset_contains(&op->od->lb_ips_v4, nat->external_ip)) {
> +                build_lswitch_rport_arp_req_flow(
> +                    nat->external_ip, AF_INET, sw_op, sw_od, 80, lflows,
> +                    stage_hint);
> +            }
> +        }
> +    }
> +

I think this looks good.  Do you mind posting it as v4 please?  Like
that it will also trigger 0-day bot to push it to its fork and run CI.

Thanks,
Dumitru

>      for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
>          build_lswitch_rport_arp_req_flow(
>              op->lrp_networks.ipv4_addrs[i].addr_s, AF_INET, sw_op, sw_od,
> 80,
> 
> 
>>
>>>
>>> diff --git a/northd/northd.c b/northd/northd.c
>>> index e9cb906e2..d93870e25 100644
>>> --- a/northd/northd.c
>>> +++ b/northd/northd.c
>>> @@ -8974,6 +8974,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
>> *op,
>>>          }
>>>      }
>>>
>>> +    struct sset nat_ips_v4 = SSET_INITIALIZER(&nat_ips_v4);
>>> +    struct sset nat_ips_v6 = SSET_INITIALIZER(&nat_ips_v6);
>>> +
>>>      for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
>>>          struct ovn_nat *nat_entry = &op->od->nat_entries[i];
>>>          const struct nbrec_nat *nat = nat_entry->nb;
>>> @@ -8982,8 +8985,16 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
>>> *op,
>>>              continue;
>>>          }
>>>
>>> -        if (!strcmp(nat->type, "snat")) {
>>> -            continue;
>>> +        if (nat_entry_is_v6(nat_entry)) {
>>> +            if (sset_contains(&nat_ips_v6, nat->external_ip)) {
>>> +                continue;
>>> +            }
>>> +            sset_add(&nat_ips_v6, nat->external_ip);
>>> +        } else {
>>> +            if (sset_contains(&nat_ips_v4, nat->external_ip)) {
>>> +                continue;
>>> +            }
>>> +            sset_add(&nat_ips_v4, nat->external_ip);
>>>          }
>>>
>>>          /* Check if the ovn port has a network configured on which we
>> could
>>> @@ -9025,6 +9036,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port
>> *op,
>>>      if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
>>>          build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
>> lflows);
>>>      }
>>> +
>>> +    sset_destroy(&nat_ips_v4);
>>> +    sset_destroy(&nat_ips_v6);
>>>  }
>>>
>>
>> Regards,
>> Dumitru
>>
>>
>
diff mbox series

Patch

diff --git a/northd/northd.c b/northd/northd.c
index e9cb906e2..99fb30f46 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -8974,6 +8974,9 @@  build_lswitch_rport_arp_req_flows(struct ovn_port *op,
         }
     }
 
+    struct sset snat_ips_v4 = SSET_INITIALIZER(&snat_ips_v4);
+    struct sset snat_ips_v6 = SSET_INITIALIZER(&snat_ips_v6);
+
     for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
         struct ovn_nat *nat_entry = &op->od->nat_entries[i];
         const struct nbrec_nat *nat = nat_entry->nb;
@@ -8983,7 +8986,17 @@  build_lswitch_rport_arp_req_flows(struct ovn_port *op,
         }
 
         if (!strcmp(nat->type, "snat")) {
-            continue;
+            if (nat_entry_is_v6(nat_entry)) {
+                if (sset_contains(&snat_ips_v6, nat->external_ip)) {
+                    continue;
+                }
+                sset_add(&snat_ips_v6, nat->external_ip);
+            } else {
+                if (sset_contains(&snat_ips_v4, nat->external_ip)) {
+                    continue;
+                }
+                sset_add(&snat_ips_v4, nat->external_ip);
+            }
         }
 
         /* Check if the ovn port has a network configured on which we could
@@ -9025,6 +9038,9 @@  build_lswitch_rport_arp_req_flows(struct ovn_port *op,
     if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
         build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od, lflows);
     }
+
+    sset_destroy(&snat_ips_v4);
+    sset_destroy(&snat_ips_v6);
 }
 
 static void
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 5c9da811f..953e0d829 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -5084,6 +5084,7 @@  AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
 ])
@@ -5098,6 +5099,7 @@  AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed 's/table=../table=??/' | sort],
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:02:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.2.1), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.100), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.200), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:201), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
 ])
 
@@ -5118,8 +5120,10 @@  AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
 ])
 
@@ -5133,7 +5137,9 @@  AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | sed 's/table=../table=??/' | sort],
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:02:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.2.1), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.100), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 20.0.0.200), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 40.0.0.100), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 40.0.0.200), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:201), action=(clone {outport = "ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
 ])
 
@@ -5151,9 +5157,11 @@  AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
 ])
 
@@ -5169,9 +5177,11 @@  AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
 ])
 
@@ -5193,9 +5203,11 @@  AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | sed 's/table=../table=??/' | sort],
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == {00:00:00:00:01:01} && (arp.op == 1 || rarp.op == 3 || nd_ns)), action=(outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 10.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.1), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 192.168.1.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.100), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && arp.op == 1 && arp.tpa == 30.0.0.200), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && nd_ns && nd.target == fe80::200:ff:fe00:101), action=(clone {outport = "ls1-ro1"; output; }; outport = "_MC_flood_l2"; output;)
 ])