ebb439
From ab83b9905ec3531b3d8b975f8035ca32d9254091 Mon Sep 17 00:00:00 2001
ebb439
From: Dumitru Ceara <dceara@redhat.com>
ebb439
Date: Tue, 3 Nov 2020 16:51:21 +0100
ebb439
Subject: [PATCH 2/2] ovn-northd: Limit self originated ARP/ND broadcast
ebb439
 domain.
ebb439
ebb439
Considering the following large scale deployment:
ebb439
external-network -- public-logical-switch -- router-1 -- sw1 -- VIF-1
ebb439
                                          -- router-2 -- sw2 -- VIF-2
ebb439
                                          ...
ebb439
                                          -- router-n -- swn -- VIF-n
ebb439
ebb439
To avoid hitting the max number of OVS resubmits (4K currently) OVN already
ebb439
restricted the broadcast domain for ARP/ND requests received from the
ebb439
external network and targeting an OVN-owned IP (e.g., owned by router-x).
ebb439
Such packets are not flooded in the broadcast domain of the public logical
ebb439
switch and instead are forwarded only to the port connecting router-x.
ebb439
ebb439
However, the max number of OVS resubmits can also be hit in the following
ebb439
scenarios:
ebb439
- router-x tries to resolve an IP owned by router-y (e.g., VIF-x trying to
ebb439
  reach VIF-y via floating IP).
ebb439
- router-x tries to resolve an IP owned by the external network.
ebb439
ebb439
Because ARP/ND requests in the above cases are originated by OVN router ports
ebb439
they were being flooded in the complete broadcast domain of the public
ebb439
logical switch.
ebb439
ebb439
Instead, we now create a new Multicast_Group for each logical switch and add
ebb439
all non-router ports to it.  ARP/ND requests are now forwarded to the router
ebb439
port that owns the IP (if any) and then flooded in this restricted MC_FLOOD_L2
ebb439
broadcast domain.
ebb439
ebb439
Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.")
ebb439
Acked-by: Mark Michelson <mmichels@redhat.com>
ebb439
Signed-off-by: Dumitru Ceara <dceara@redhat.com>
ebb439
Signed-off-by: Numan Siddique <numans@ovn.org>
ebb439
(cherry picked from upstream commit 8c6a5bc21847dab8ccbe18ab1e4b563ddca13379)
ebb439
ebb439
Change-Id: Ic35a6e7531d09374c0b3ba9c8be3bd986adc7941
ebb439
---
ebb439
 lib/mcast-group-index.h |  1 +
ebb439
 northd/ovn-northd.8.xml | 19 +++++------
ebb439
 northd/ovn-northd.c     | 84 ++++++++++++++++++++++++++++++-------------------
ebb439
 tests/ovn.at            | 50 ++++++++++++++---------------
ebb439
 4 files changed, 86 insertions(+), 68 deletions(-)
ebb439
ebb439
diff --git a/lib/mcast-group-index.h b/lib/mcast-group-index.h
ebb439
index ba995ba..72af117 100644
ebb439
--- a/lib/mcast-group-index.h
ebb439
+++ b/lib/mcast-group-index.h
ebb439
@@ -30,6 +30,7 @@ enum ovn_mcast_tunnel_keys {
ebb439
     OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY,
ebb439
     OVN_MCAST_MROUTER_STATIC_TUNNEL_KEY,
ebb439
     OVN_MCAST_STATIC_TUNNEL_KEY,
ebb439
+    OVN_MCAST_FLOOD_L2_TUNNEL_KEY,
ebb439
     OVN_MIN_IP_MULTICAST,
ebb439
     OVN_MAX_IP_MULTICAST = OVN_MAX_MULTICAST,
ebb439
 };
ebb439
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
ebb439
index f1c7c9b..8206982 100644
ebb439
--- a/northd/ovn-northd.8.xml
ebb439
+++ b/northd/ovn-northd.8.xml
ebb439
@@ -1367,18 +1367,19 @@ output;
ebb439
       
ebb439
 
ebb439
       
  • ebb439
    -        Priority-80 flows for each port connected to a logical router
    ebb439
    -        matching self originated GARP/ARP request/ND packets. These packets
    ebb439
    -        are flooded to the MC_FLOOD which contains all logical
    ebb439
    -        ports.
    ebb439
    +        Priority-80 flows for each IP address/VIP/NAT address owned by a
    ebb439
    +        router port connected to the switch. These flows match ARP requests
    ebb439
    +        and ND packets for the specific IP addresses.  Matched packets are
    ebb439
    +        forwarded only to the router that owns the IP address and to the
    ebb439
    +        MC_FLOOD_L2 multicast group which contains all non-router
    ebb439
    +        logical ports.
    ebb439
           
    ebb439
     
    ebb439
           
  • ebb439
    -        Priority-75 flows for each IP address/VIP/NAT address owned by a
    ebb439
    -        router port connected to the switch. These flows match ARP requests
    ebb439
    -        and ND packets for the specific IP addresses.  Matched packets are
    ebb439
    -        forwarded only to the router that owns the IP address and, if
    ebb439
    -        present, to the localnet port of the logical switch.
    ebb439
    +        Priority-75 flows for each port connected to a logical router
    ebb439
    +        matching self originated ARP request/ND packets.  These packets
    ebb439
    +        are flooded to the MC_FLOOD_L2 which contains all
    ebb439
    +        non-router logical ports.
    ebb439
           
    ebb439
     
    ebb439
           
  • ebb439
    diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
    ebb439
    index ecbe98e..ce291ec 100644
    ebb439
    --- a/northd/ovn-northd.c
    ebb439
    +++ b/northd/ovn-northd.c
    ebb439
    @@ -1493,6 +1493,12 @@ lsp_is_external(const struct nbrec_logical_switch_port *nbsp)
    ebb439
     }
    ebb439
     
    ebb439
     static bool
    ebb439
    +lsp_is_router(const struct nbrec_logical_switch_port *nbsp)
    ebb439
    +{
    ebb439
    +    return !strcmp(nbsp->type, "router");
    ebb439
    +}
    ebb439
    +
    ebb439
    +static bool
    ebb439
     lrport_is_enabled(const struct nbrec_logical_router_port *lrport)
    ebb439
     {
    ebb439
         return !lrport->enabled || *lrport->enabled;
    ebb439
    @@ -2424,7 +2430,7 @@ join_logical_ports(struct northd_context *ctx,
    ebb439
          * to their peers. */
    ebb439
         struct ovn_port *op;
    ebb439
         HMAP_FOR_EACH (op, key_node, ports) {
    ebb439
    -        if (op->nbsp && !strcmp(op->nbsp->type, "router") && !op->derived) {
    ebb439
    +        if (op->nbsp && lsp_is_router(op->nbsp) && !op->derived) {
    ebb439
                 const char *peer_name = smap_get(&op->nbsp->options, "router-port");
    ebb439
                 if (!peer_name) {
    ebb439
                     continue;
    ebb439
    @@ -3105,7 +3111,7 @@ ovn_port_update_sbrec(struct northd_context *ctx,
    ebb439
     
    ebb439
             sbrec_port_binding_set_nat_addresses(op->sb, NULL, 0);
    ebb439
         } else {
    ebb439
    -        if (strcmp(op->nbsp->type, "router")) {
    ebb439
    +        if (!lsp_is_router(op->nbsp)) {
    ebb439
                 uint32_t queue_id = smap_get_int(
    ebb439
                         &op->sb->options, "qdisc_queue_id", 0);
    ebb439
                 bool has_qos = port_has_qos_params(&op->nbsp->options);
    ebb439
    @@ -3808,6 +3814,10 @@ static const struct multicast_group mc_static =
    ebb439
     static const struct multicast_group mc_unknown =
    ebb439
         { MC_UNKNOWN, OVN_MCAST_UNKNOWN_TUNNEL_KEY };
    ebb439
     
    ebb439
    +#define MC_FLOOD_L2 "_MC_flood_l2"
    ebb439
    +static const struct multicast_group mc_flood_l2 =
    ebb439
    +    { MC_FLOOD_L2, OVN_MCAST_FLOOD_L2_TUNNEL_KEY };
    ebb439
    +
    ebb439
     static bool
    ebb439
     multicast_group_equal(const struct multicast_group *a,
    ebb439
                           const struct multicast_group *b)
    ebb439
    @@ -6372,12 +6382,11 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op,
    ebb439
             sset_add(&all_eth_addrs, nat->external_mac);
    ebb439
         }
    ebb439
     
    ebb439
    -
    ebb439
    -    /* Self originated (G)ARP requests/ND need to be flooded as usual.
    ebb439
    -     * Determine that packets are self originated by also matching on
    ebb439
    -     * source MAC. Matching on ingress port is not reliable in case this
    ebb439
    -     * is a VLAN-backed network.
    ebb439
    -     * Priority: 80.
    ebb439
    +    /* Self originated ARP requests/ND need to be flooded to the L2 domain
    ebb439
    +     * (except on router ports).  Determine that packets are self originated
    ebb439
    +     * by also matching on source MAC. Matching on ingress port is not
    ebb439
    +     * reliable in case this is a VLAN-backed network.
    ebb439
    +     * Priority: 75.
    ebb439
          */
    ebb439
         const char *eth_addr;
    ebb439
     
    ebb439
    @@ -6393,7 +6402,7 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op,
    ebb439
                       ds_cstr(&eth_src));
    ebb439
         ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
    ebb439
                       ds_cstr(&match),
    ebb439
    -                  "outport = \""MC_FLOOD"\"; output;");
    ebb439
    +                  "outport = \""MC_FLOOD_L2"\"; output;");
    ebb439
     
    ebb439
         sset_destroy(&all_eth_addrs);
    ebb439
         ds_destroy(&eth_src);
    ebb439
    @@ -6439,14 +6448,16 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
    ebb439
         ds_chomp(&match, ',');
    ebb439
         ds_put_cstr(&match, "}");
    ebb439
     
    ebb439
    -    /* Send a the packet only to the router pipeline and skip flooding it
    ebb439
    -     * in the broadcast domain (except for the localnet port).
    ebb439
    +    /* Send a the packet to the router pipeline.  If the switch has non-router
    ebb439
    +     * ports then flood it there as well.
    ebb439
          */
    ebb439
    -    for (size_t i = 0; i < od->n_localnet_ports; i++) {
    ebb439
    -        ds_put_format(&actions, "clone { outport = %s; output; }; ",
    ebb439
    -                      od->localnet_ports[i]->json_key);
    ebb439
    +    if (od->n_router_ports != od->nbs->n_ports) {
    ebb439
    +        ds_put_format(&actions, "clone {outport = %s; output; }; "
    ebb439
    +                                "outport = \""MC_FLOOD_L2"\"; output;",
    ebb439
    +                      patch_op->json_key);
    ebb439
    +    } else {
    ebb439
    +        ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
    ebb439
         }
    ebb439
    -    ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
    ebb439
         ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
    ebb439
                                 ds_cstr(&match), ds_cstr(&actions), stage_hint);
    ebb439
     
    ebb439
    @@ -6476,14 +6487,9 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
    ebb439
             return;
    ebb439
         }
    ebb439
     
    ebb439
    -    /* Self originated (G)ARP requests/ND need to be flooded as usual.
    ebb439
    -     * Priority: 80.
    ebb439
    -     */
    ebb439
    -    build_lswitch_rport_arp_req_self_orig_flow(op, 80, sw_od, lflows);
    ebb439
    -
    ebb439
         /* Forward ARP requests for owned IP addresses (L3, VIP, NAT) only to this
    ebb439
          * router port.
    ebb439
    -     * Priority: 75.
    ebb439
    +     * Priority: 80.
    ebb439
          */
    ebb439
         struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4);
    ebb439
         struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6);
    ebb439
    @@ -6558,17 +6564,28 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
    ebb439
     
    ebb439
         if (!sset_is_empty(&all_ips_v4)) {
    ebb439
             build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op,
    ebb439
    -                                                sw_od, 75, lflows,
    ebb439
    +                                                sw_od, 80, lflows,
    ebb439
                                                     stage_hint);
    ebb439
         }
    ebb439
         if (!sset_is_empty(&all_ips_v6)) {
    ebb439
             build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op,
    ebb439
    -                                                sw_od, 75, lflows,
    ebb439
    +                                                sw_od, 80, lflows,
    ebb439
                                                     stage_hint);
    ebb439
         }
    ebb439
     
    ebb439
         sset_destroy(&all_ips_v4);
    ebb439
         sset_destroy(&all_ips_v6);
    ebb439
    +
    ebb439
    +    /* Self originated ARP requests/ND need to be flooded as usual.
    ebb439
    +     *
    ebb439
    +     * However, if the switch doesn't have any non-router ports we shouldn't
    ebb439
    +     * even try to flood.
    ebb439
    +     *
    ebb439
    +     * Priority: 75.
    ebb439
    +     */
    ebb439
    +    if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
    ebb439
    +        build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od, lflows);
    ebb439
    +    }
    ebb439
     }
    ebb439
     
    ebb439
     static void
    ebb439
    @@ -6908,7 +6925,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
    ebb439
                  *  - port type is localport
    ebb439
                  */
    ebb439
                 if (check_lsp_is_up &&
    ebb439
    -                !lsp_is_up(op->nbsp) && strcmp(op->nbsp->type, "router") &&
    ebb439
    +                !lsp_is_up(op->nbsp) && !lsp_is_router(op->nbsp) &&
    ebb439
                     strcmp(op->nbsp->type, "localport")) {
    ebb439
                     continue;
    ebb439
                 }
    ebb439
    @@ -6983,8 +7000,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
    ebb439
                                 "flags.loopback = 1; "
    ebb439
                                 "output; "
    ebb439
                                 "};",
    ebb439
    -                            !strcmp(op->nbsp->type, "router") ?
    ebb439
    -                                "nd_na_router" : "nd_na",
    ebb439
    +                            lsp_is_router(op->nbsp) ? "nd_na_router" : "nd_na",
    ebb439
                                 op->lsp_addrs[i].ea_s,
    ebb439
                                 op->lsp_addrs[i].ipv6_addrs[j].addr_s,
    ebb439
                                 op->lsp_addrs[i].ipv6_addrs[j].addr_s,
    ebb439
    @@ -7066,7 +7082,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
    ebb439
                continue;
    ebb439
             }
    ebb439
     
    ebb439
    -        if (!lsp_is_enabled(op->nbsp) || !strcmp(op->nbsp->type, "router")) {
    ebb439
    +        if (!lsp_is_enabled(op->nbsp) || lsp_is_router(op->nbsp)) {
    ebb439
                 /* Don't add the DHCP flows if the port is not enabled or if the
    ebb439
                  * port is a router port. */
    ebb439
                 continue;
    ebb439
    @@ -7326,7 +7342,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
    ebb439
              * broadcast flooding of ARP/ND requests in table 19. We direct the
    ebb439
              * requests only to the router port that owns the IP address.
    ebb439
              */
    ebb439
    -        if (!strcmp(op->nbsp->type, "router")) {
    ebb439
    +        if (lsp_is_router(op->nbsp)) {
    ebb439
                 build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows,
    ebb439
                                                   &op->nbsp->header_);
    ebb439
             }
    ebb439
    @@ -10786,7 +10802,7 @@ build_arp_resolve_flows_for_lrouter_port(
    ebb439
                                             &op->nbrp->header_);
    ebb439
                 }
    ebb439
             }
    ebb439
    -    } else if (op->od->n_router_ports && strcmp(op->nbsp->type, "router")
    ebb439
    +    } else if (op->od->n_router_ports && !lsp_is_router(op->nbsp)
    ebb439
                    && strcmp(op->nbsp->type, "virtual")) {
    ebb439
             /* This is a logical switch port that backs a VM or a container.
    ebb439
              * Extract its addresses. For each of the address, go through all
    ebb439
    @@ -10870,7 +10886,7 @@ build_arp_resolve_flows_for_lrouter_port(
    ebb439
                     }
    ebb439
                 }
    ebb439
             }
    ebb439
    -    } else if (op->od->n_router_ports && strcmp(op->nbsp->type, "router")
    ebb439
    +    } else if (op->od->n_router_ports && !lsp_is_router(op->nbsp)
    ebb439
                    && !strcmp(op->nbsp->type, "virtual")) {
    ebb439
             /* This is a virtual port. Add ARP replies for the virtual ip with
    ebb439
              * the mac of the present active virtual parent.
    ebb439
    @@ -10974,7 +10990,7 @@ build_arp_resolve_flows_for_lrouter_port(
    ebb439
                     }
    ebb439
                 }
    ebb439
             }
    ebb439
    -    } else if (!strcmp(op->nbsp->type, "router")) {
    ebb439
    +    } else if (lsp_is_router(op->nbsp)) {
    ebb439
             /* This is a logical switch port that connects to a router. */
    ebb439
     
    ebb439
             /* The peer of this switch port is the router port for which
    ebb439
    @@ -11929,6 +11945,10 @@ build_mcast_groups(struct northd_context *ctx,
    ebb439
             } else if (op->nbsp && lsp_is_enabled(op->nbsp)) {
    ebb439
                 ovn_multicast_add(mcast_groups, &mc_flood, op);
    ebb439
     
    ebb439
    +            if (!lsp_is_router(op->nbsp)) {
    ebb439
    +                ovn_multicast_add(mcast_groups, &mc_flood_l2, op);
    ebb439
    +            }
    ebb439
    +
    ebb439
                 /* If this port is connected to a multicast router then add it
    ebb439
                  * to the MC_MROUTER_FLOOD group.
    ebb439
                  */
    ebb439
    @@ -12372,7 +12392,7 @@ handle_port_binding_changes(struct northd_context *ctx, struct hmap *ports,
    ebb439
                 continue;
    ebb439
             }
    ebb439
     
    ebb439
    -        bool up = (sb->chassis || !strcmp(op->nbsp->type, "router"));
    ebb439
    +        bool up = (sb->chassis || lsp_is_router(op->nbsp));
    ebb439
             if (!op->nbsp->up || *op->nbsp->up != up) {
    ebb439
                 nbrec_logical_switch_port_set_up(op->nbsp, &up, 1);
    ebb439
             }
    ebb439
    diff --git a/tests/ovn.at b/tests/ovn.at
    ebb439
    index ea4a6da..180fb91 100644
    ebb439
    --- a/tests/ovn.at
    ebb439
    +++ b/tests/ovn.at
    ebb439
    @@ -3626,7 +3626,7 @@ test_ip() {
    ebb439
         done
    ebb439
     }
    ebb439
     
    ebb439
    -# test_arp INPORT SHA SPA TPA FLOOD [REPLY_HA]
    ebb439
    +# test_arp INPORT SHA SPA TPA [REPLY_HA]
    ebb439
     #
    ebb439
     # Causes a packet to be received on INPORT.  The packet is an ARP
    ebb439
     # request with SHA, SPA, and TPA as specified.  If REPLY_HA is provided, then
    ebb439
    @@ -3637,25 +3637,21 @@ test_ip() {
    ebb439
     # SHA and REPLY_HA are each 12 hex digits.
    ebb439
     # SPA and TPA are each 8 hex digits.
    ebb439
     test_arp() {
    ebb439
    -    local inport=$1 sha=$2 spa=$3 tpa=$4 flood=$5 reply_ha=$6
    ebb439
    +    local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
    ebb439
         local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
    ebb439
         hv=hv`vif_to_hv $inport`
    ebb439
         as $hv ovs-appctl netdev-dummy/receive vif$inport $request
    ebb439
         as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request
    ebb439
     
    ebb439
         # Expect to receive the broadcast ARP on the other logical switch ports if
    ebb439
    -    # IP address is not configured on the switch patch port or on the router
    ebb439
    -    # port (i.e, $flood == 1).
    ebb439
    +    # IP address is not configured to the switch patch port.
    ebb439
         local i=`vif_to_ls $inport`
    ebb439
         local j k
    ebb439
         for j in 1 2 3; do
    ebb439
             for k in 1 2 3; do
    ebb439
    -            # Skip ingress port.
    ebb439
    -            if test $i$j$k == $inport; then
    ebb439
    -                continue
    ebb439
    -            fi
    ebb439
    -
    ebb439
    -            if test X$flood == X1; then
    ebb439
    +            # 192.168.33.254 is configured to the switch patch port for lrp33,
    ebb439
    +            # so no ARP flooding expected for it.
    ebb439
    +            if test $i$j$k != $inport && test $tpa != `ip_to_hex 192 168 33 254`; then
    ebb439
                     echo $request >> $i$j$k.expected
    ebb439
                 fi
    ebb439
             done
    ebb439
    @@ -3792,9 +3788,9 @@ for i in 1 2 3; do
    ebb439
           otherip=`ip_to_hex 192 168 $i$j 55` # Some other IP in subnet
    ebb439
           externalip=`ip_to_hex 1 2 3 4`      # Some other IP not in subnet
    ebb439
     
    ebb439
    -      test_arp $i$j$k $smac $sip        $rip       0     $rmac       #4
    ebb439
    -      test_arp $i$j$k $smac $otherip    $rip       0     $rmac       #5
    ebb439
    -      test_arp $i$j$k $smac $sip        $otherip   1                 #6
    ebb439
    +      test_arp $i$j$k $smac $sip        $rip        $rmac      #4
    ebb439
    +      test_arp $i$j$k $smac $otherip    $rip        $rmac      #5
    ebb439
    +      test_arp $i$j$k $smac $sip        $otherip               #6
    ebb439
     
    ebb439
           # When rip is 192.168.33.254, ARP request from externalip won't be
    ebb439
           # filtered, because 192.168.33.254 is configured to switch peer port
    ebb439
    @@ -3803,7 +3799,7 @@ for i in 1 2 3; do
    ebb439
           if test $i = 3 && test $j = 3; then
    ebb439
             lrp33_rsp=$rmac
    ebb439
           fi
    ebb439
    -      test_arp $i$j$k $smac $externalip $rip       0      $lrp33_rsp #7
    ebb439
    +      test_arp $i$j$k $smac $externalip $rip        $lrp33_rsp #7
    ebb439
     
    ebb439
           # MAC binding should be learned from ARP request.
    ebb439
           host_mac_pretty=f0:00:00:00:0$i:$j$k
    ebb439
    @@ -19895,7 +19891,7 @@ match_r1_metadata="metadata=0x${r1_dp_key}"
    ebb439
     send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1)
    ebb439
     
    ebb439
     # Verify that the ARP request is sent only to rtr1.
    ebb439
    -match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.1,arp_op=1"
    ebb439
    +match_arp_req="priority=80.*${match_sw_metadata}.*arp_tpa=10.0.0.1,arp_op=1"
    ebb439
     match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15"
    ebb439
     match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15"
    ebb439
     
    ebb439
    @@ -19919,7 +19915,7 @@ dst_ipv6=00100000000000000000000000000001
    ebb439
     send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
    ebb439
     
    ebb439
     # Verify that the ND_NS is sent only to rtr1.
    ebb439
    -match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::1"
    ebb439
    +match_nd_ns="priority=80.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::1"
    ebb439
     
    ebb439
     as hv1
    ebb439
     OVS_WAIT_UNTIL([
    ebb439
    @@ -19951,7 +19947,7 @@ ovn-nbctl --wait=hv sync
    ebb439
     send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11)
    ebb439
     
    ebb439
     # Verify that the ARP request is sent only to rtr1.
    ebb439
    -match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.11,arp_op=1"
    ebb439
    +match_arp_req="priority=80.*${match_sw_metadata}.*arp_tpa=10.0.0.11,arp_op=1"
    ebb439
     match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15"
    ebb439
     match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15"
    ebb439
     
    ebb439
    @@ -19975,7 +19971,7 @@ dst_ipv6=00100000000000000000000000000011
    ebb439
     send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
    ebb439
     
    ebb439
     # Verify that the ND_NS is sent only to rtr1.
    ebb439
    -match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::11"
    ebb439
    +match_nd_ns="priority=80.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::11"
    ebb439
     
    ebb439
     as hv1
    ebb439
     OVS_WAIT_UNTIL([
    ebb439
    @@ -20015,7 +20011,7 @@ ovn-nbctl --wait=hv sync
    ebb439
     # - 10.0.0.22, 10::22 - LB VIPs.
    ebb439
     # - 10.0.0.222, 10::222 - DNAT IPs.
    ebb439
     as hv1
    ebb439
    -AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw_metadata}" | grep -oE "arp_tpa=[[0-9.]]+" | sort], [0], [dnl
    ebb439
    +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw_metadata}" | grep -oE "arp_tpa=[[0-9.]]+" | sort], [0], [dnl
    ebb439
     arp_tpa=10.0.0.1
    ebb439
     arp_tpa=10.0.0.11
    ebb439
     arp_tpa=10.0.0.111
    ebb439
    @@ -20025,7 +20021,7 @@ arp_tpa=10.0.0.2
    ebb439
     arp_tpa=10.0.0.22
    ebb439
     arp_tpa=10.0.0.222
    ebb439
     ])
    ebb439
    -AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw_metadata}" | grep -oE "nd_target=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
    +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw_metadata}" | grep -oE "nd_target=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
     nd_target=10::1
    ebb439
     nd_target=10::11
    ebb439
     nd_target=10::111
    ebb439
    @@ -20041,10 +20037,10 @@ nd_target=fe80::200:ff:fe00:200
    ebb439
     # For sw1-rtr1:
    ebb439
     # - 20.0.0.1, 20::1, fe80::200:1ff:fe00:0 - interface IPs.
    ebb439
     as hv1
    ebb439
    -AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw1_metadata}" | grep -oE "arp_tpa=[[0-9.]]+" | sort], [0], [dnl
    ebb439
    +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw1_metadata}" | grep -oE "arp_tpa=[[0-9.]]+" | sort], [0], [dnl
    ebb439
     arp_tpa=20.0.0.1
    ebb439
     ])
    ebb439
    -AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw1_metadata}" | grep -oE "nd_target=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
    +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw1_metadata}" | grep -oE "nd_target=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
     nd_target=20::1
    ebb439
     nd_target=fe80::200:1ff:fe00:0
    ebb439
     ])
    ebb439
    @@ -20056,13 +20052,13 @@ nd_target=fe80::200:1ff:fe00:0
    ebb439
     # - 00:00:00:01:00:00 - dnat_and_snat external MAC.
    ebb439
     # - 00:00:00:02:00:00 - dnat_and_snat external MAC.
    ebb439
     as hv1
    ebb439
    -AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw_metadata}.*arp_op=1" | grep -oE "dl_src=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
    +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw_metadata}.*arp_op=1" | grep -oE "dl_src=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
     dl_src=00:00:00:00:01:00
    ebb439
     dl_src=00:00:00:00:02:00
    ebb439
     dl_src=00:00:00:01:00:00
    ebb439
     dl_src=00:00:00:02:00:00
    ebb439
     ])
    ebb439
    -AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw_metadata}.*icmp_type=135" | grep -oE "dl_src=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
    +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw_metadata}.*icmp_type=135" | grep -oE "dl_src=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
     dl_src=00:00:00:00:01:00
    ebb439
     dl_src=00:00:00:00:02:00
    ebb439
     dl_src=00:00:00:01:00:00
    ebb439
    @@ -20072,7 +20068,7 @@ dl_src=00:00:00:02:00:00
    ebb439
     # Self originated ARP/NS with SMACs owned by rtr1-sw1 should be flooded:
    ebb439
     # - 00:00:01:00:00:00 - interface MAC.
    ebb439
     as hv1
    ebb439
    -AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=80,.*${match_sw1_metadata}.*arp_op=1" | grep -oE "dl_src=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
    +AT_CHECK([ovs-ofctl dump-flows br-int | grep -E "priority=75,.*${match_sw1_metadata}.*arp_op=1" | grep -oE "dl_src=[[0-9a-f:]]+" | sort], [0], [dnl
    ebb439
     dl_src=00:00:01:00:00:00
    ebb439
     ])
    ebb439
     
    ebb439
    @@ -20080,7 +20076,7 @@ dl_src=00:00:01:00:00:00
    ebb439
     send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111)
    ebb439
     
    ebb439
     # Verify that the ARP request is sent only to rtr1.
    ebb439
    -match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.111,arp_op=1"
    ebb439
    +match_arp_req="priority=80.*${match_sw_metadata}.*arp_tpa=10.0.0.111,arp_op=1"
    ebb439
     match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15"
    ebb439
     match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15"
    ebb439
     
    ebb439
    @@ -20144,7 +20140,7 @@ dst_ipv6=00100000000000000000000000000111
    ebb439
     send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
    ebb439
     
    ebb439
     # Verify that the ND_NS is sent only to rtr1.
    ebb439
    -match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::111"
    ebb439
    +match_nd_ns="priority=80.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::111"
    ebb439
     
    ebb439
     as hv1
    ebb439
     OVS_WAIT_UNTIL([
    ebb439
    -- 
    ebb439
    1.8.3.1
    ebb439