Logical switches should not forward IP multicast packets towards
connected OVN logical routers if the router is not configured to relay
IP multicast traffic (LR.options:mcast_relay=false).

Previously, a single priority 71 flow matched "eth.mcast && (arp || ip)"
and sent all such traffic to _MC_flood, which includes all ports
(including router ports).  This caused IP multicast packets to be
forwarded to routers that would always drop them in lr_in_ip_input,
wasting CPU cycles.  In topologies with hundreds of routers connected
to logical switches, this can lead to the max resubmit limit (4K) being
hit by such packets, unnecessarily overloading ovs-vswitchd.

Fix this by splitting the flow into three:
 - Priority 72: "eth.mcast && (nd_na || nd_rs || nd_ra)" -> _MC_flood
   ND NA, Router Solicitation and Router Advertisement must still reach
   routers: ND NA for neighbor learning, ND RS so routers can respond
   with Router Advertisements, ND RA for proper IPv6 network operation.
 - Priority 71: "eth.mcast && arp" -> _MC_flood
   ARP (including gratuitous ARP) still reaches all ports.
 - Priority 71: "eth.mcast && ip" -> _MC_flood_l2
   IP multicast is only sent to non-router L2 ports, plus
   _MC_mrouter_flood (if any connected router has relay enabled)
   and _MC_static (if any port has mcast_flood=true).

Reported-at: https://redhat.atlassian.net/browse/FDP-2262
Assisted-by: Claude, with model: claude-opus-4-6
Signed-off-by: Dumitru Ceara <[email protected]>
---
 northd/northd.c         |  37 +++++++-
 northd/ovn-northd.8.xml |  33 +++++--
 tests/ovn-northd.at     | 191 ++++++++++++++++++++++++++++++++++++----
 3 files changed, 237 insertions(+), 24 deletions(-)

diff --git a/northd/northd.c b/northd/northd.c
index b7239f4e20..92c1707817 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -11020,10 +11020,45 @@ build_lswitch_destination_lookup_bmcast(struct 
ovn_datapath *od,
                       lflow_ref);
     }
 
+    /* ND NA, Router Solicitation and Router Advertisement should be
+     * flooded to all ports including routers.  ND NA is needed for
+     * neighbor learning; ND RS must reach routers so they can respond
+     * with Router Advertisements; ND RA must reach routers for proper
+     * IPv6 network operation.
+     */
+    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 72,
+                  "eth.mcast && (nd_na || nd_rs || nd_ra)",
+                  "outport = \""MC_FLOOD"\"; output;", lflow_ref);
+
+    /* ARP multicast should be flooded to all ports including routers. */
     ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 71,
-                  "eth.mcast && (arp || ip)",
+                  "eth.mcast && arp",
                   "outport = \""MC_FLOOD"\"; output;", lflow_ref);
 
+    /* IP multicast should not be forwarded to routers that don't have
+     * IGMP relay enabled (mcast_relay=true).  Such routers will always
+     * drop IP multicast in lr_in_ip_input anyway.
+     */
+    ds_clear(actions);
+    if (mcast_sw_info->flood_relay) {
+        ds_put_cstr(actions,
+                    "clone { "
+                        "outport = \""MC_MROUTER_FLOOD"\"; "
+                        "output; "
+                    "}; ");
+    }
+    if (mcast_sw_info->flood_static) {
+        ds_put_cstr(actions,
+                    "clone { "
+                        "outport = \""MC_STATIC"\"; "
+                        "output; "
+                    "}; ");
+    }
+    ds_put_cstr(actions, "outport = \""MC_FLOOD_L2"\"; output;");
+    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 71,
+                  "eth.mcast && ip",
+                  ds_cstr(actions), lflow_ref);
+
     /* Non-{arp,ip} L2 multicast traffic should not be sent to router
      * ports since these packets will be discarded in the router pipeline.
      */
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index 9f2d118dd4..4d6370da6b 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -2414,18 +2414,41 @@ output;
       </li>
 
       <li>
-        A priority-72 flow that outputs all ARP requests and ND packets with
-        an Ethernet broadcast or multicast <code>eth.dst</code> to the
-        <code>MC_FLOOD_L2</code> multicast group if
-        <code>other_config:broadcast-arps-to-all-routers=true</code>.
+        A priority-72 flow that outputs all ND NA (Neighbor Advertisement),
+        ND RS (Router Solicitation) and ND RA (Router Advertisement) packets
+        with an Ethernet broadcast or multicast <code>eth.dst</code> to the
+        <code>MC_FLOOD</code> multicast group, which includes all ports.
+        ND NA must reach routers for neighbor learning; ND RS must reach
+        routers so they can respond with Router Advertisements; ND RA must
+        reach routers for proper IPv6 network operation.
       </li>
 
       <li>
-        A priority-71 flow that outputs all ARP or IP packets with an Ethernet
+        A priority-72 flow that outputs all ARP requests and ND NS (Neighbor
+        Solicitation) packets with an Ethernet broadcast or multicast
+        <code>eth.dst</code> to the <code>MC_FLOOD_L2</code> multicast group
+        if <code>other_config:broadcast-arps-to-all-routers=false</code>.
+      </li>
+
+      <li>
+        A priority-71 flow that outputs all ARP packets with an Ethernet
         broadcast or multicast <code>eth.dst</code> to the
         <code>MC_FLOOD</code> multicast group.
       </li>
 
+      <li>
+        A priority-71 flow that outputs all IP packets with an Ethernet
+        broadcast or multicast <code>eth.dst</code> to the
+        <code>MC_FLOOD_L2</code> multicast group, which contains only
+        non-router logical ports.  If any connected router has
+        <code>options:mcast_relay=true</code>, the packet is also cloned to
+        the <code>MC_MROUTER_FLOOD</code> multicast group (which contains
+        only the router ports with relay enabled).  If any port has
+        <code>options:mcast_flood=true</code>, it is also cloned to the
+        <code>MC_STATIC</code> multicast group.  This prevents IP multicast
+        from being unnecessarily forwarded to routers that would drop it.
+      </li>
+
       <li>
         A priority-70 flow that outputs all packets with an Ethernet broadcast
         or multicast <code>eth.dst</code> to the <code>MC_FLOOD_L2</code>
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 6230fd039a..7c2f53da69 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -5863,7 +5863,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(outport = "ls1-ro1"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:02), action=(outport = "vm1"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:01:01} && eth.dst == ff:ff:ff:ff:ff:ff && (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.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;)
@@ -5877,7 +5879,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:01), action=(outport = "ls2-ro2"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:02), action=(outport = "vm2"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:02:01} && eth.dst == ff:ff:ff:ff:ff:ff && (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 && 
nd_ns && nd.target == fe80::200:ff:fe00:201), action=(clone {outport = 
"ls2-ro2"; output; }; outport = "_MC_flood_l2"; output;)
@@ -5899,7 +5903,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(outport = "ls1-ro1"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:02), action=(outport = "vm1"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:01:01} && eth.dst == ff:ff:ff:ff:ff:ff && (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;)
@@ -5915,7 +5921,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:01), action=(outport = "ls2-ro2"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:02), action=(outport = "vm2"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:02:01} && eth.dst == ff:ff:ff:ff:ff:ff && (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;)
@@ -5939,7 +5947,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(outport = "ls1-ro1"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:02), action=(outport = "vm1"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:01:01} && eth.dst == ff:ff:ff:ff:ff:ff && (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;)
@@ -5957,7 +5967,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls2_lflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:01), action=(outport = "ls2-ro2"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:02), action=(outport = "vm2"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:02:01} && eth.dst == ff:ff:ff:ff:ff:ff && (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;)
@@ -5980,7 +5992,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(outport = "ls1-ro1"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:02), action=(outport = "vm1"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:01:01} && eth.dst == ff:ff:ff:ff:ff:ff && (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;)
@@ -6002,7 +6016,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(outport = "ls1-ro1"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:02), action=(outport = "vm1"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:01:01} && eth.dst == ff:ff:ff:ff:ff:ff && (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;)
@@ -6031,7 +6047,9 @@ AT_CHECK([grep "ls_in_l2_lkup" ls1_lflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01 && is_chassis_resident("cr-ro1-ls1")), action=(outport = 
"ls1-ro1"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:02), action=(outport = "vm1"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:01:01} && eth.dst == ff:ff:ff:ff:ff:ff && (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;)
@@ -8365,6 +8383,127 @@ OVN_CLEANUP_NORTHD
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([IP multicast flood without IGMP relay])
+AT_KEYWORDS([mcast_relay])
+ovn_start
+
+check ovn-nbctl lr-add lr0
+check ovn-nbctl ls-add ls0
+
+check ovn-nbctl lrp-add lr0 lrp0 00:00:00:00:00:01 10.0.0.1/24 fe80::1/64
+check ovn-nbctl lsp-add-router-port ls0 lsp0-router lrp0
+
+check ovn-nbctl lsp-add ls0 lsp0 -- \
+    lsp-set-addresses lsp0 "00:00:00:00:00:02 10.0.0.2"
+
+dnl IP multicast packet from lsp0.
+mcast_pkt='inport == "lsp0" && eth.src == 00:00:00:00:00:02 && eth.dst == 
01:00:5e:00:01:2a && ip4.src == 10.0.0.2 && ip4.dst == 239.0.1.42 && ip.ttl == 
64 && udp.src == 42 && udp.dst == 43'
+
+dnl ARP broadcast from lsp0.
+arp_pkt='inport == "lsp0" && eth.src == 00:00:00:00:00:02 && eth.dst == 
ff:ff:ff:ff:ff:ff && arp.op == 1 && arp.sha == 00:00:00:00:00:02 && arp.spa == 
10.0.0.2 && arp.tha == 00:00:00:00:00:00 && arp.tpa == 10.0.0.3'
+
+dnl Gratuitous ND NA multicast from lsp0.
+nd_na_pkt='inport == "lsp0" && eth.src == 00:00:00:00:00:02 && eth.dst == 
33:33:00:00:00:01 && ip6.src == fe80::200:ff:fe00:2 && ip6.dst == ff02::1 && 
icmp6.type == 136 && nd.target == fe80::200:ff:fe00:2'
+
+dnl Router solicitation multicast from lsp0.
+nd_rs_pkt='inport == "lsp0" && eth.src == 00:00:00:00:00:02 && eth.dst == 
33:33:00:00:00:02 && ip6.src == fe80::200:ff:fe00:2 && ip6.dst == ff02::2 && 
icmp6.type == 133 && ip.ttl == 255'
+
+dnl Router advertisement multicast from lsp0.
+nd_ra_pkt='inport == "lsp0" && eth.src == 00:00:00:00:00:02 && eth.dst == 
33:33:00:00:00:01 && ip6.src == fe80::200:ff:fe00:2 && ip6.dst == ff02::1 && 
icmp6.type == 134 && ip.ttl == 255'
+
+dnl Scenario 1: default, no relay, no snooping.
+dnl The eth.mcast && ip flow should use _MC_flood_l2 not _MC_flood.
+check ovn-nbctl --wait=sb sync
+
+ovn-sbctl dump-flows ls0 > lsflows
+AT_CAPTURE_FILE([lsflows])
+AT_CHECK([grep -e 'ls_in_l2_lkup' lsflows | grep -e 'priority=71' -e 
'priority=72' | ovn_strip_lflows], [0], [dnl
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
+])
+
+dnl Verify packet forwarding: IP multicast should not reach the router
+dnl port, while ARP, ND NA, ND RS and ND RA should.
+AT_CHECK([ovn_trace ls0 "$mcast_pkt" | grep 'outport="lsp0-router"'], [1])
+AT_CHECK([ovn_trace ls0 "$arp_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_na_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_rs_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_ra_pkt" | grep -q 'outport="lsp0-router"'])
+
+dnl Scenario 2: relay enabled on the router but snooping not enabled on the 
switch.
+dnl No priority 80 flow is installed because build_mcast_flood_lswitch returns
+dnl early when snooping is disabled, so the priority 71 flow is the only path
+dnl for IP multicast to reach relay-enabled routers via _MC_mrouter_flood.
+check ovn-nbctl set logical_router lr0 options:mcast_relay="true"
+check ovn-nbctl --wait=sb sync
+
+ovn-sbctl dump-flows ls0 > lsflows2
+AT_CAPTURE_FILE([lsflows2])
+AT_CHECK([grep -e 'ls_in_l2_lkup' lsflows2 | grep -e 'priority=71' -e 
'priority=72' | ovn_strip_lflows], [0], [dnl
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(clone { outport = "_MC_mrouter_flood"; output; }; outport = 
"_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
+])
+
+dnl Verify packet forwarding: all packet types should reach the router port.
+AT_CHECK([ovn_trace ls0 "$mcast_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$arp_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_na_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_rs_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_ra_pkt" | grep -q 'outport="lsp0-router"'])
+
+dnl Scenario 3: relay enabled on the router and snooping enabled on the switch.
+dnl The eth.mcast && ip flow should include _MC_mrouter_flood for relay-enabled
+dnl router ports plus _MC_flood_l2 for regular ports.
+check ovn-nbctl set logical_switch ls0 other_config:mcast_snoop="true"
+check ovn-nbctl --wait=sb sync
+
+ovn-sbctl dump-flows ls0 > lsflows3
+AT_CAPTURE_FILE([lsflows3])
+AT_CHECK([grep -e 'ls_in_l2_lkup' lsflows3 | grep -e 'priority=71' -e 
'priority=72' | ovn_strip_lflows], [0], [dnl
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(clone { outport = "_MC_mrouter_flood"; output; }; outport = 
"_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
+])
+
+dnl Verify packet forwarding: all packet types should reach the router port.
+AT_CHECK([ovn_trace ls0 "$mcast_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$arp_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_na_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_rs_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_ra_pkt" | grep -q 'outport="lsp0-router"'])
+
+dnl Scenario 4: also enable flood_unregistered on the switch.
+dnl The priority 71 flow should still have _MC_mrouter_flood and _MC_flood_l2.
+dnl There should be no priority 80 flow for ip4.mcast || ip6.mcast because
+dnl flood_unregistered causes build_mcast_flood_lswitch to return early.
+check ovn-nbctl set logical_switch ls0 
other_config:mcast_flood_unregistered="true"
+check ovn-nbctl --wait=sb sync
+
+ovn-sbctl dump-flows ls0 > lsflows4
+AT_CAPTURE_FILE([lsflows4])
+AT_CHECK([grep -e 'ls_in_l2_lkup' lsflows4 | grep -e 'priority=71' -e 
'priority=72' | ovn_strip_lflows], [0], [dnl
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(clone { outport = "_MC_mrouter_flood"; output; }; outport = 
"_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
+])
+
+dnl Verify there is no priority 80 flow for ip4.mcast || ip6.mcast when 
flood_unregistered is set.
+AT_CHECK([grep -e 'ls_in_l2_lkup' lsflows4 | grep -e 'priority=80' | grep -F 
'ip4.mcast || ip6.mcast'], [1])
+
+dnl Verify packet forwarding: all packet types should reach the router port.
+AT_CHECK([ovn_trace ls0 "$mcast_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$arp_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_na_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_rs_pkt" | grep -q 'outport="lsp0-router"'])
+AT_CHECK([ovn_trace ls0 "$nd_ra_pkt" | grep -q 'outport="lsp0-router"'])
+
+OVN_CLEANUP_NORTHD
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD_NO_HV_PARALLELIZATION([
 AT_SETUP([ACLs after lb])
 AT_KEYWORDS([acl])
@@ -9678,7 +9817,9 @@ ovn_strip_lflows ], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=100  , match=(reg8[[23]] == 1), 
action=(output;)
   table=??(ls_in_l2_lkup      ), priority=110  , match=(eth.dst == 
$svc_monitor_mac && (tcp || icmp || icmp6)), action=(handle_svc_check(inport);)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
   table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), 
action=(drop;)
   table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
@@ -9714,7 +9855,9 @@ ovn_strip_lflows ], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(outport = "sw0p1"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:02), action=(outport = "sw0p2"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
   table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), 
action=(drop;)
   table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
@@ -9749,7 +9892,9 @@ ovn_strip_lflows ], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(outport = "sw0p1"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:02), action=(outport = "sw0p2"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
   table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), 
action=(drop;)
   table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
@@ -9785,7 +9930,9 @@ ovn_strip_lflows ], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(drop;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:02), action=(outport = "sw0p2"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
   table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), 
action=(drop;)
   table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "sw0p1"), 
action=(drop;)
@@ -9821,7 +9968,9 @@ ovn_strip_lflows ], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(drop;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:02), action=(outport = "sw0p2"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
   table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), 
action=(drop;)
   table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "sw0p1"), 
action=(drop;)
@@ -9861,7 +10010,9 @@ ovn_strip_lflows ], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:01:01), action=(outport = "sw0p1"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:02:02), action=(outport = "sw0p2"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_unknown   ), priority=0    , match=(1), action=(output;)
   table=??(ls_in_l2_unknown   ), priority=50   , match=(outport == "none"), 
action=(drop;)
   table=??(ls_out_apply_port_sec), priority=0    , match=(1), action=(output;)
@@ -14201,7 +14352,9 @@ AT_CHECK([grep "ls_in_l2_lkup" publicflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:ff:02 && is_chassis_resident("cr-lr0-public")), action=(outport = 
"public-lr0"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
30:54:00:00:00:03 && is_chassis_resident("sw0-port1")), action=(outport = 
"public-lr0"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:ff:02, 30:54:00:00:00:03} && eth.dst == ff:ff:ff:ff:ff:ff && 
(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 == 172.168.0.10), action=(clone {outport = "public-lr0"; 
output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && 
arp.op == 1 && arp.tpa == 172.168.0.100), action=(clone {outport = 
"public-lr0"; output; }; outport = "_MC_flood_l2"; output;)
@@ -14379,7 +14532,9 @@ AT_CHECK([grep "ls_in_l2_lkup" publicflows | 
ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
00:00:00:00:ff:02 && is_chassis_resident("cr-public-lr0")), action=(outport = 
"public-lr0"; output;)
   table=??(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 
30:54:00:00:00:03 && is_chassis_resident("sw0-port1")), action=(outport = 
"public-lr0"; output;)
   table=??(ls_in_l2_lkup      ), priority=70   , match=(eth.mcast), 
action=(outport = "_MC_flood_l2"; output;)
-  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && (arp || 
ip)), action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && arp), 
action=(outport = "_MC_flood"; output;)
+  table=??(ls_in_l2_lkup      ), priority=71   , match=(eth.mcast && ip), 
action=(outport = "_MC_flood_l2"; output;)
+  table=??(ls_in_l2_lkup      ), priority=72   , match=(eth.mcast && (nd_na || 
nd_rs || nd_ra)), action=(outport = "_MC_flood"; output;)
   table=??(ls_in_l2_lkup      ), priority=75   , match=(eth.src == 
{00:00:00:00:ff:02, 30:54:00:00:00:03} && eth.dst == ff:ff:ff:ff:ff:ff && 
(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 == 172.168.0.10 && 
!is_chassis_resident("cr-public-lr0")), action=(clone {outport = 
"cr-public-lr0"; output; }; outport = "_MC_flood_l2"; output;)
   table=??(ls_in_l2_lkup      ), priority=80   , match=(flags[[1]] == 0 && 
arp.op == 1 && arp.tpa == 172.168.0.10 && 
is_chassis_resident("cr-public-lr0")), action=(clone {outport = "public-lr0"; 
output; }; outport = "_MC_flood_l2"; output;)
@@ -14541,7 +14696,7 @@ AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup 
| grep -E "tcp.dst == 179
 AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup | grep "arp.op == 2 && 
arp.tpa == 172.16.1.1" | ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=100  , match=(arp.op == 2 && arp.tpa 
== 172.16.1.1), action=(clone { outport = "lsp-bgp"; output; }; outport = 
"ls-lr"; output;)
 ])
-AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup | grep "&& nd_na" | 
ovn_strip_lflows], [0], [dnl
+AT_CHECK([ovn-sbctl dump-flows ls | grep ls_in_l2_lkup | grep "ip6.dst == .* 
&& nd_na" | ovn_strip_lflows], [0], [dnl
   table=??(ls_in_l2_lkup      ), priority=100  , match=(ip6.dst == 
fe80::ac:10ff:fe01:1 && nd_na), action=(clone { outport = "lsp-bgp"; output; }; 
outport = "ls-lr"; output;)
 ])
 
-- 
2.53.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to