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
