On Tue, Nov 12, 2019 at 2:29 AM Dumitru Ceara <dce...@redhat.com> wrote: > > ARP request and ND NS packets for router owned IPs were being > flooded in the complete L2 domain (using the MC_FLOOD multicast group). > However this creates a scaling issue in scenarios where aggregation > logical switches are connected to more logical routers (~350). The > logical pipelines of all routers would have to be executed before the > packet is finally replied to by a single router, the owner of the IP > address. > > This commit limits the broadcast domain by bypassing the L2 Lookup stage > for ARP requests that will be replied by a single router. The packets > are forwarded only to the router port that owns the target IP address. > > IPs that are owned by the routers and for which this fix applies are: > - IP addresses configured on the router ports. > - VIPs. > - NAT IPs. > > Reported-at: https://bugzilla.redhat.com/1756945 > Reported-by: Anil Venkata <vkomm...@redhat.com> > Signed-off-by: Dumitru Ceara <dce...@redhat.com> > > --- > v7: > - Address Han's comments: > - Remove flooding for all ARPs received on VLAN networks. To avoid > that we now identify self originated (G)ARPs by matching on source > MAC address too. > - Rename REGBIT_NOT_VXLAN to FLAGBIT_NOT_VXLAN. > - Fix ovn-sb manpage. > - Split patch in a series of 2: > - patch1: fixes the get_router_load_balancer_ips() function. > - patch2: limits the ARP/ND broadcast domain. > v6: > - Address Han's comments: > - remove flooding of ARPs targeting OVN owned IP addresses. > - update ovn-architecture documentation. > - rename ARP handling functions. > - Adapt "ovn -- 3 HVs, 3 LS, 3 lports/LS, 1 LR" autotest to take into > account the new way of forwarding ARPs. > - Also, properly deal with ARP packets on VLAN-backed networks. > v5: Address Numan's comments: update comments & make autotest more > robust. > v4: Rebase. > v3: Properly deal with VXLAN traffic. Address review comments from > Numan (add autotests). Fix function get_router_load_balancer_ips. > Rebase -> deal with IPv6 NAT too. > v2: Move ARP broadcast domain limiting to table S_SWITCH_IN_L2_LKUP to > address localnet ports too. > --- > northd/ovn-northd.8.xml | 14 ++ > northd/ovn-northd.c | 230 +++++++++++++++++++++++++++++++---- > ovn-architecture.7.xml | 19 +++ > tests/ovn.at | 307 +++++++++++++++++++++++++++++++++++++++++++++-- > 4 files changed, 530 insertions(+), 40 deletions(-) > > diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml > index 0a33dcd..344cc0d 100644 > --- a/northd/ovn-northd.8.xml > +++ b/northd/ovn-northd.8.xml > @@ -1005,6 +1005,20 @@ output; > </li> > > <li> > + Priority-80 flows for each port connected to a logical router > + matching self originated GARP/ARP request/ND packets. These packets > + are flooded to the <code>MC_FLOOD</code> which contains all logical > + ports. > + </li> > + > + <li> > + Priority-75 flows for each IP address/VIP/NAT address owned by a > + router port connected to the switch. These flows match ARP requests > + and ND packets for the specific IP addresses. Matched packets are > + forwarded only to the router that owns the IP address. > + </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</code> > multicast group. > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c > index 32f3200..d6beb97 100644 > --- a/northd/ovn-northd.c > +++ b/northd/ovn-northd.c > @@ -210,6 +210,8 @@ enum ovn_stage { > #define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[4]" > #define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[5]" > > +#define FLAGBIT_NOT_VXLAN "flags[1] == 0" > + > /* Returns an "enum ovn_stage" built from the arguments. */ > static enum ovn_stage > ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline pipeline, > @@ -1202,6 +1204,34 @@ ovn_port_allocate_key(struct ovn_datapath *od) > 1, (1u << 15) - 1, &od->port_key_hint); > } > > +/* Returns true if the logical switch port 'enabled' column is empty or > + * set to true. Otherwise, returns false. */ > +static bool > +lsp_is_enabled(const struct nbrec_logical_switch_port *lsp) > +{ > + return !lsp->n_enabled || *lsp->enabled; > +} > + > +/* Returns true only if the logical switch port 'up' column is set to true. > + * Otherwise, if the column is not set or set to false, returns false. */ > +static bool > +lsp_is_up(const struct nbrec_logical_switch_port *lsp) > +{ > + return lsp->n_up && *lsp->up; > +} > + > +static bool > +lsp_is_external(const struct nbrec_logical_switch_port *nbsp) > +{ > + return !strcmp(nbsp->type, "external"); > +} > + > +static bool > +lrport_is_enabled(const struct nbrec_logical_router_port *lrport) > +{ > + return !lrport->enabled || *lrport->enabled; > +} > + > static char * > chassis_redirect_name(const char *port_name) > { > @@ -3750,28 +3780,6 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op, > > } > > -/* Returns true if the logical switch port 'enabled' column is empty or > - * set to true. Otherwise, returns false. */ > -static bool > -lsp_is_enabled(const struct nbrec_logical_switch_port *lsp) > -{ > - return !lsp->n_enabled || *lsp->enabled; > -} > - > -/* Returns true only if the logical switch port 'up' column is set to true. > - * Otherwise, if the column is not set or set to false, returns false. */ > -static bool > -lsp_is_up(const struct nbrec_logical_switch_port *lsp) > -{ > - return lsp->n_up && *lsp->up; > -} > - > -static bool > -lsp_is_external(const struct nbrec_logical_switch_port *nbsp) > -{ > - return !strcmp(nbsp->type, "external"); > -} > - > static bool > build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip, > struct ds *options_action, struct ds *response_action, > @@ -5174,6 +5182,170 @@ build_lrouter_groups(struct hmap *ports, struct ovs_list *lr_list) > } > } > > +/* > + * Ingress table 17: Flows that flood self originated ARP/ND packets in the > + * switching domain. > + */ > +static void > +build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op, > + uint32_t priority, > + struct ovn_datapath *od, > + struct hmap *lflows) > +{ > + struct ds match = DS_EMPTY_INITIALIZER; > + struct ds eth_src = DS_EMPTY_INITIALIZER; > + > + /* Self originated (G)ARP requests/ND need to be flooded as usual. > + * Determine that packets are self originated by also matching on > + * source MAC. Matching on ingress port is not reliable in case this > + * is a VLAN-backed network. > + * Priority: 80. > + */ > + ds_put_format(ð_src, "{ %s, ", op->lrp_networks.ea_s); > + for (size_t i = 0; i < op->od->nbr->n_nat; i++) { > + const struct nbrec_nat *nat = op->od->nbr->nat[i]; > + > + if (!nat->external_mac) { > + continue; > + } > + > + ds_put_format(ð_src, "%s, ", nat->external_mac); > + }
As discussed we need to add chassis unique MAC that are configured in external-ids:ovn-chassis-mac-mappings of Chassis records, but I didn't find this in the patch. VLAN backed logical router may not work without this. > + ds_chomp(ð_src, ' '); > + ds_chomp(ð_src, ','); > + ds_put_cstr(ð_src, "}"); > + > + ds_put_format(&match, "eth.src == %s && (arp.op == 1 || nd_ns)", > + ds_cstr(ð_src)); > + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority, > + ds_cstr(&match), > + "outport = \""MC_FLOOD"\"; output;"); > + > + ds_destroy(&match); > + ds_destroy(ð_src); > +} > + > +/* > + * Ingress table 17: Flows that forward ARP/ND requests only to the routers > + * that own the addresses. Other ARP/ND packets are still flooded in the > + * switching domain as regular broadcast. > + */ > +static void > +build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips, > + int addr_family, > + struct ovn_port *patch_op, > + struct ovn_datapath *od, > + uint32_t priority, > + struct hmap *lflows) > +{ > + struct ds match = DS_EMPTY_INITIALIZER; > + struct ds actions = DS_EMPTY_INITIALIZER; > + > + /* Packets received from VXLAN tunnels have already been through the > + * router pipeline so we should skip them. Normally this is done by the > + * multicast_group implementation (VXLAN packets skip table 32 which > + * delivers to patch ports) but we're bypassing multicast_groups. > + */ > + ds_put_cstr(&match, FLAGBIT_NOT_VXLAN " && "); > + > + if (addr_family == AF_INET) { > + ds_put_cstr(&match, "arp.op == 1 && arp.tpa == { "); > + } else { > + ds_put_cstr(&match, "nd_ns && nd.target == { "); > + } > + > + const char *ip_address; > + SSET_FOR_EACH (ip_address, ips) { > + ds_put_format(&match, "%s, ", ip_address); > + } > + > + ds_chomp(&match, ' '); > + ds_chomp(&match, ','); > + ds_put_cstr(&match, "}"); > + > + /* Send a the packet only to the router pipeline and skip flooding it > + * in the broadcast domain. > + */ > + ds_put_format(&actions, "outport = %s; output;", patch_op->json_key); > + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority, > + ds_cstr(&match), ds_cstr(&actions)); > + > + ds_destroy(&match); > + ds_destroy(&actions); > +} > + > +/* > + * Ingress table 17: Flows that forward ARP/ND requests only to the routers > + * that own the addresses. > + * Priorities: > + * - 80: self originated GARPs that need to follow regular processing. > + * - 75: ARP requests to router owned IPs (interface IP/LB/NAT). > + */ > +static void > +build_lswitch_rport_arp_req_flows(struct ovn_port *op, > + struct ovn_datapath *sw_od, > + struct ovn_port *sw_op, > + struct hmap *lflows) > +{ > + if (!op || !op->nbrp) { > + return; > + } > + > + if (!lrport_is_enabled(op->nbrp)) { > + return; > + } > + > + /* Self originated (G)ARP requests/ND need to be flooded as usual. > + * Priority: 80. > + */ > + build_lswitch_rport_arp_req_self_orig_flow(op, 80, sw_od, lflows); > + > + /* Forward ARP requests for owned IP addresses (L3, VIP, NAT) only to this > + * router port. > + * Priority: 75. > + */ > + struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); > + struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); > + > + for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { > + sset_add(&all_ips_v4, op->lrp_networks.ipv4_addrs[i].addr_s); > + } > + for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { > + sset_add(&all_ips_v6, op->lrp_networks.ipv6_addrs[i].addr_s); > + } > + > + get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6); > + > + for (size_t i = 0; i < op->od->nbr->n_nat; i++) { > + const struct nbrec_nat *nat = op->od->nbr->nat[i]; > + > + if (!strcmp(nat->type, "snat")) { > + continue; > + } > + > + ovs_be32 ip; > + ovs_be32 mask; > + struct in6_addr ipv6; > + struct in6_addr mask_v6; > + > + if (ip_parse_masked(nat->external_ip, &ip, &mask)) { > + if (!ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6)) { > + sset_add(&all_ips_v6, nat->external_ip); > + } > + } else { > + sset_add(&all_ips_v4, nat->external_ip); > + } > + } > + > + build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op, > + sw_od, 75, lflows); > + build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op, > + sw_od, 75, lflows); > + > + sset_destroy(&all_ips_v4); > + sset_destroy(&all_ips_v6); > +} > + > static void > build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, > struct hmap *port_groups, struct hmap *lflows, > @@ -5761,6 +5933,14 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, > continue; > } > > + /* For ports connected to logical routers add flows to bypass the > + * broadcast flooding of ARP/ND requests in table 17. We direct the > + * requests only to the router port that owns the IP address. > + */ > + if (!strcmp(op->nbsp->type, "router")) { > + build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows); > + } > + > for (size_t i = 0; i < op->nbsp->n_addresses; i++) { > /* Addresses are owned by the logical port. > * Ethernet address followed by zero or more IPv4 > @@ -5892,12 +6072,6 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, > ds_destroy(&actions); > } > > -static bool > -lrport_is_enabled(const struct nbrec_logical_router_port *lrport) > -{ > - return !lrport->enabled || *lrport->enabled; > -} > - > /* Returns a string of the IP address of the router port 'op' that > * overlaps with 'ip_s". If one is not found, returns NULL. > * > diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml > index 7966b65..c43f16d 100644 > --- a/ovn-architecture.7.xml > +++ b/ovn-architecture.7.xml > @@ -1390,6 +1390,25 @@ > http://docs.openvswitch.org/en/latest/topics/high-availability. > </p> > > + <h3>ARP request and ND NS packet processing</h3> > + > + <p> > + Due to the fact that ARP requests and ND NA packets are usually broadcast > + packets, for performance reasons, OVN deals with requests that target OVN > + owned IP addresses (i.e., IP addresses configured on the router ports, > + VIPs, NAT IPs) in a specific way and only forwards them to the logical > + router that owns the target IP address. This behavior is different than > + that of traditional swithces and implies that other routers/hosts > + connected to the logical switch will not learn the MAC/IP binding from > + the request packet. > + </p> > + > + <p> > + All other ARP and ND packets are flooded in the L2 broadcast domain and > + to all attached logical patch ports. > + </p> > + > + > <h2>Multiple localnet logical switches connected to a Logical Router</h2> > > <p> > diff --git a/tests/ovn.at b/tests/ovn.at > index 3e429e3..26e33d2 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -2877,7 +2877,7 @@ test_ip() { > done > } > > -# test_arp INPORT SHA SPA TPA [REPLY_HA] > +# test_arp INPORT SHA SPA TPA FLOOD [REPLY_HA] > # > # Causes a packet to be received on INPORT. The packet is an ARP > # request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then > @@ -2888,21 +2888,25 @@ test_ip() { > # SHA and REPLY_HA are each 12 hex digits. > # SPA and TPA are each 8 hex digits. > test_arp() { > - local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5 > + local inport=$1 sha=$2 spa=$3 tpa=$4 flood=$5 reply_ha=$6 > local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa} > hv=hv`vif_to_hv $inport` > as $hv ovs-appctl netdev-dummy/receive vif$inport $request > as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request > > # Expect to receive the broadcast ARP on the other logical switch ports if > - # IP address is not configured to the switch patch port. > + # IP address is not configured on the switch patch port or on the router > + # port (i.e, $flood == 1). > local i=`vif_to_ls $inport` > local j k > for j in 1 2 3; do > for k in 1 2 3; do > - # 192.168.33.254 is configured to the switch patch port for lrp33, > - # so no ARP flooding expected for it. > - if test $i$j$k != $inport && test $tpa != `ip_to_hex 192 168 33 254`; then > + # Skip ingress port. > + if test $i$j$k == $inport; then > + continue > + fi > + > + if test X$flood == X1; then > echo $request >> $i$j$k.expected > fi > done > @@ -3039,9 +3043,9 @@ for i in 1 2 3; do > otherip=`ip_to_hex 192 168 $i$j 55` # Some other IP in subnet > externalip=`ip_to_hex 1 2 3 4` # Some other IP not in subnet > > - test_arp $i$j$k $smac $sip $rip $rmac #4 > - test_arp $i$j$k $smac $otherip $rip $rmac #5 > - test_arp $i$j$k $smac $sip $otherip #6 > + test_arp $i$j$k $smac $sip $rip 0 $rmac #4 > + test_arp $i$j$k $smac $otherip $rip 0 $rmac #5 > + test_arp $i$j$k $smac $sip $otherip 1 #6 > > # When rip is 192.168.33.254, ARP request from externalip won't be > # filtered, because 192.168.33.254 is configured to switch peer port > @@ -3050,7 +3054,7 @@ for i in 1 2 3; do > if test $i = 3 && test $j = 3; then > lrp33_rsp=$rmac > fi > - test_arp $i$j$k $smac $externalip $rip $lrp33_rsp #7 > + test_arp $i$j$k $smac $externalip $rip 0 $lrp33_rsp #7 > > # MAC binding should be learned from ARP request. > host_mac_pretty=f0:00:00:00:0$i:$j$k > @@ -9595,7 +9599,7 @@ ovn-nbctl --wait=hv --timeout=3 sync > # Check that there is a logical flow in logical switch foo's pipeline > # to set the outport to rp-foo (which is expected). > OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \ > -grep rp-foo | grep -v is_chassis_resident | wc -l`]) > +grep rp-foo | grep -v is_chassis_resident | grep priority=50 -c`]) > > # Set the option 'reside-on-redirect-chassis' for foo > ovn-nbctl set logical_router_port foo options:reside-on-redirect-chassis=true > @@ -9603,7 +9607,7 @@ ovn-nbctl set logical_router_port foo options:reside-on-redirect-chassis=true > # to set the outport to rp-foo with the condition is_chassis_redirect. > ovn-sbctl dump-flows foo > OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \ > -grep rp-foo | grep is_chassis_resident | wc -l`]) > +grep rp-foo | grep is_chassis_resident | grep priority=50 -c`]) > > echo "---------NB dump-----" > ovn-nbctl show > @@ -16694,3 +16698,282 @@ as hv4 ovs-appctl fdb/show br-phys > OVN_CLEANUP([hv1],[hv2],[hv3],[hv4]) > > AT_CLEANUP > + > +AT_SETUP([ovn -- ARP/ND request broadcast limiting]) > +AT_SKIP_IF([test $HAVE_PYTHON = no]) > +ovn_start > + > +ip_to_hex() { > + printf "%02x%02x%02x%02x" "$@" > +} > + > +send_arp_request() { > + local hv=$1 inport=$2 eth_src=$3 spa=$4 tpa=$5 > + local eth_dst=ffffffffffff > + local eth_type=0806 > + local eth=${eth_dst}${eth_src}${eth_type} > + > + local arp=0001080006040001${eth_src}${spa}${eth_dst}${tpa} > + > + local request=${eth}${arp} > + as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request > +} > + > +send_nd_ns() { > + local hv=$1 inport=$2 eth_src=$3 spa=$4 tpa=$5 cksum=$6 > + > + local eth_dst=ffffffffffff > + local eth_type=86dd > + local eth=${eth_dst}${eth_src}${eth_type} > + > + local ip_vhlen=60000000 > + local ip_plen=0020 > + local ip_next=3a > + local ip_ttl=ff > + local ip=${ip_vhlen}${ip_plen}${ip_next}${ip_ttl}${spa}${tpa} > + > + # Neighbor Solicitation > + local icmp6_type=87 > + local icmp6_code=00 > + local icmp6_rsvd=00000000 > + # ICMPv6 source lla option > + local icmp6_opt=01 > + local icmp6_optlen=01 > + local icmp6=${icmp6_type}${icmp6_code}${cksum}${icmp6_rsvd}${tpa}${icmp6_opt}${icmp6_optlen}${eth_src} > + > + local request=${eth}${ip}${icmp6} > + > + as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request > +} > + > +src_mac=000000000001 > + > +net_add n1 > +sim_add hv1 > +as hv1 > +ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.1 > + > +ovs-vsctl -- add-port br-int hv1-vif1 -- \ > + set interface hv1-vif1 external-ids:iface-id=sw-agg-ext \ > + options:tx_pcap=hv1/vif1-tx.pcap \ > + options:rxq_pcap=hv1/vif1-rx.pcap \ > + ofport-request=1 > + > +# One Aggregation Switch connected to two Logical networks (routers). > +ovn-nbctl ls-add sw-agg > +ovn-nbctl lsp-add sw-agg sw-agg-ext \ > + -- lsp-set-addresses sw-agg-ext 00:00:00:00:00:01 > + > +ovn-nbctl lsp-add sw-agg sw-rtr1 \ > + -- lsp-set-type sw-rtr1 router \ > + -- lsp-set-addresses sw-rtr1 00:00:00:00:01:00 \ > + -- lsp-set-options sw-rtr1 router-port=rtr1-sw > +ovn-nbctl lsp-add sw-agg sw-rtr2 \ > + -- lsp-set-type sw-rtr2 router \ > + -- lsp-set-addresses sw-rtr2 00:00:00:00:02:00 \ > + -- lsp-set-options sw-rtr2 router-port=rtr2-sw > + > +# Configure L3 interface IPv4 & IPv6 on both routers > +ovn-nbctl lr-add rtr1 > +ovn-nbctl lrp-add rtr1 rtr1-sw 00:00:00:00:01:00 10.0.0.1/24 10::1/64 > + > +ovn-nbctl lr-add rtr2 > +ovn-nbctl lrp-add rtr2 rtr2-sw 00:00:00:00:02:00 10.0.0.2/24 10::2/64 > + > +OVN_POPULATE_ARP > +ovn-nbctl --wait=hv sync > + > +sw_dp_uuid=$(ovn-sbctl --bare --columns _uuid list datapath_binding sw-agg) > +sw_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding sw-agg) > + > +r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1) > +r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2) > + > +mc_key=$(ovn-sbctl --bare --columns tunnel_key find multicast_group datapath=${sw_dp_uuid} name="_MC_flood") > +mc_key=$(printf "%04x" $mc_key) > + > +match_sw_metadata="metadata=0x${sw_dp_key}" > + > +# Inject ARP request for first router owned IP address. > +send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1) > + > +# Verify that the ARP request is sent only to rtr1. > +match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.1,arp_op=1" > +match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15" > +match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15" > + > +as hv1 > +OVS_WAIT_UNTIL([ > + pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \ > + grep n_packets=1 -c) > + test "1" = "${pkts_to_rtr1}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_arp_req}" | grep "${match_send_rtr2}" | \ > + grep n_packets=1 -c) > + test "0" = "${pkts_to_rtr2}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_flooded=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) > + test "0" = "${pkts_flooded}" > +]) > + > +# Inject ND_NS for ofirst router owned IP address. > +src_ipv6=00100000000000000000000000000254 > +dst_ipv6=00100000000000000000000000000001 > +send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d > + > +# Verify that the ND_NS is sent only to rtr1. > +match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::1" > + > +as hv1 > +OVS_WAIT_UNTIL([ > + pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_nd_ns}" | grep "${match_send_rtr1}" | \ > + grep n_packets=1 -c) > + test "1" = "${pkts_to_rtr1}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_nd_ns}" | grep "${match_send_rtr2}" | \ > + grep n_packets=1 -c) > + test "0" = "${pkts_to_rtr2}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_flooded=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) > + test "0" = "${pkts_flooded}" > +]) > + > +# Configure load balancing on both routers. > +ovn-nbctl lb-add lb1-v4 10.0.0.11 42.42.42.1 > +ovn-nbctl lb-add lb1-v6 10::11 42::1 > +ovn-nbctl lr-lb-add rtr1 lb1-v4 > +ovn-nbctl lr-lb-add rtr1 lb1-v6 > + > +ovn-nbctl lb-add lb2-v4 10.0.0.22 42.42.42.2 > +ovn-nbctl lb-add lb2-v6 10::22 42::2 > +ovn-nbctl lr-lb-add rtr2 lb2-v4 > +ovn-nbctl lr-lb-add rtr2 lb2-v6 > +ovn-nbctl --wait=hv sync > + > +# Inject ARP request for first router owned VIP address. > +send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11) > + > +# Verify that the ARP request is sent only to rtr1. > +match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.11,arp_op=1" > +match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15" > +match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15" > + > +as hv1 > +OVS_WAIT_UNTIL([ > + pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \ > + grep n_packets=1 -c) > + test "1" = "${pkts_to_rtr1}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_arp_req}" | grep "${match_send_rtr2}" | \ > + grep n_packets=1 -c) > + test "0" = "${pkts_to_rtr2}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_flooded=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) > + test "0" = "${pkts_flooded}" > +]) > + > +# Inject ND_NS for first router owned VIP address. > +src_ipv6=00100000000000000000000000000254 > +dst_ipv6=00100000000000000000000000000011 > +send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d > + > +# Verify that the ND_NS is sent only to rtr1. > +match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::11" > + > +as hv1 > +OVS_WAIT_UNTIL([ > + pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_nd_ns}" | grep "${match_send_rtr1}" | \ > + grep n_packets=1 -c) > + test "1" = "${pkts_to_rtr1}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_nd_ns}" | grep "${match_send_rtr2}" | \ > + grep n_packets=1 -c) > + test "0" = "${pkts_to_rtr2}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_flooded=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) > + test "0" = "${pkts_flooded}" > +]) > + > +# Configure NAT on both routers > +ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.111 42.42.42.1 > +ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::111 42::1 > +ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10.0.0.222 42.42.42.2 > +ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10::222 42::2 > + > +# Inject ARP request for first router owned NAT address. > +send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111) > + > +# Verify that the ARP request is sent only to rtr1. > +match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.111,arp_op=1" > +match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15" > +match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15" > + > +as hv1 > +OVS_WAIT_UNTIL([ > + pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \ > + grep n_packets=1 -c) > + test "1" = "${pkts_to_rtr1}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_arp_req}" | grep "${match_send_rtr2}" | \ > + grep n_packets=1 -c) > + test "0" = "${pkts_to_rtr2}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_flooded=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) > + test "0" = "${pkts_flooded}" > +]) > + > +# Inject ND_NS for first router owned IP address. > +src_ipv6=00100000000000000000000000000254 > +dst_ipv6=00100000000000000000000000000111 > +send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d > + > +# Verify that the ND_NS is sent only to rtr1. > +match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::111" > + > +as hv1 > +OVS_WAIT_UNTIL([ > + pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_nd_ns}" | grep "${match_send_rtr1}" | \ > + grep n_packets=1 -c) > + test "1" = "${pkts_to_rtr1}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_nd_ns}" | grep "${match_send_rtr2}" | \ > + grep n_packets=1 -c) > + test "0" = "${pkts_to_rtr2}" > +]) > +OVS_WAIT_UNTIL([ > + pkts_flooded=$(ovs-ofctl dump-flows br-int | \ > + grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c) > + test "0" = "${pkts_flooded}" > +]) > + > +OVN_CLEANUP([hv1]) > +AT_CLEANUP > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev