On Wed, Jul 29, 2020 at 10:42 AM Numan Siddique <num...@ovn.org> wrote:

>
>
> On Thu, Jul 23, 2020 at 10:57 AM Han Zhou <hz...@ovn.org> wrote:
>
>> Support a new logical router option "always_learn_from_arp_request" that
>> controls
>> behavior when handling ARP requests or IPv4 ND-NS packets.
>>
>> "true" - Always learn the MAC/IP binding and add a new MAC_Binding entry
>> (default behavior)
>>
>> "false" - If there is a MAC_binding for that IP and the MAC is different,
>> or,
>> if TPA of ARP request belongs to any router port on this router, then
>> update/add that MAC/IP binding. Otherwise, don't update/add entries.
>>
>> Reported-by: Girish Moodalbail <gmoodalb...@nvidia.com>
>> Reported-at:
>> https://mail.openvswitch.org/pipermail/ovs-discuss/2020-May/049995.html
>> Signed-off-by: Han Zhou <hz...@ovn.org>
>>
>
> Hi Han,
>
> I just gave a quick look. I'll review properly tomorrow.
>
> I just have a few small comments now. I think it's good to add a few tests
> in ovn-northd.at
> to make sure that ovn-northd programs the flows as expected.
>
>
Hi Numan

This patch already updated the existing test case in ovn.at to cover the
scenario when always_learn_from_arp_request is set to false (and also flip
back and forth to verify the mac binding is updated for existed entries).
It verifies the behavior e2e, so I think maybe it is not necessary to add a
separate test case just to check the lflows. What do you think?
I sent v2 which added a test case for the first patch (it was not covered
in the v1). Please take a look:
https://patchwork.ozlabs.org/project/openvswitch/list/?series=194194

Thanks,
Han


> Thanks
> Numan
>
> ---
>>  northd/ovn-northd.8.xml | 109
>> ++++++++++++++++++++++++++++++++++++++++--------
>>  northd/ovn-northd.c     |  96 +++++++++++++++++++++++++++++++++++-------
>>  ovn-nb.xml              |  27 ++++++++++++
>>  tests/ovn.at            |  18 ++++++++
>>  4 files changed, 217 insertions(+), 33 deletions(-)
>>
>> diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
>> index 9f2c70f..30936ab 100644
>> --- a/northd/ovn-northd.8.xml
>> +++ b/northd/ovn-northd.8.xml
>> @@ -1537,58 +1537,126 @@ output;
>>          <p>
>>            For each router port <var>P</var> that owns IP address
>> <var>A</var>,
>>            which belongs to subnet <var>S</var> with prefix length
>> <var>L</var>,
>> -          a priority-100 flow is added which matches
>> -          <code>inport == <var>P</var> &amp;&amp;
>> -          arp.spa == <var>S</var>/<var>L</var> &amp;&amp; arp.op ==
>> 1</code>
>> -          (ARP request) with the
>> -          following actions:
>> +          if the option <code>always_learn_from_arp_request</code> is
>> +          <code>true</code> for this router, a priority-100 flow is
>> added which
>> +          matches <code>inport == <var>P</var> &amp;&amp; arp.spa ==
>> +          <var>S</var>/<var>L</var> &amp;&amp; arp.op == 1</code> (ARP
>> request)
>> +          with the following actions:
>> +        </p>
>> +
>> +        <pre>
>> +reg9[2] = lookup_arp(inport, arp.spa, arp.sha);
>> +next;
>> +        </pre>
>> +
>> +        <p>
>> +          If the option <code>always_learn_from_arp_request</code> is
>> +          <code>false</code>, the following two flows are added.
>> +        </p>
>> +
>> +        <p>
>> +          A priority-110 flow is added which matches <code>inport ==
>> +          <var>P</var> &amp;&amp; arp.spa == <var>S</var>/<var>L</var>
>> +          &amp;&amp; arp.tpa == <var>A</var> &amp;&amp; arp.op ==
>> 1</code>
>> +          (ARP request) with the following actions:
>>          </p>
>>
>>          <pre>
>>  reg9[2] = lookup_arp(inport, arp.spa, arp.sha);
>> +reg9[3] = 1;
>> +next;
>> +        </pre>
>> +
>> +        <p>
>> +          A priority-100 flow is added which matches <code>inport ==
>> +          <var>P</var> &amp;&amp; arp.spa == <var>S</var>/<var>L</var>
>> +          &amp;&amp; arp.op == 1</code> (ARP request) with the following
>> +          actions:
>> +        </p>
>> +
>> +        <pre>
>> +reg9[2] = lookup_arp(inport, arp.spa, arp.sha);
>> +reg9[3] = lookup_arp_ip(inport, arp.spa);
>>  next;
>>          </pre>
>>
>>          <p>
>>            If the logical router port <var>P</var> is a distributed
>> gateway
>>            router port, additional match
>> -          <code>is_chassis_resident(cr-<var>P</var>)</code> is added so
>> that
>> -          the resident gateway chassis handles the neighbor lookup.
>> +          <code>is_chassis_resident(cr-<var>P</var>)</code> is added for
>> all
>> +          these flows.
>>          </p>
>>        </li>
>>
>>        <li>
>>          <p>
>>            A priority-100 flow which matches on ARP reply packets and
>> applies
>> -          the actions:
>> +          the actions if the option
>> <code>always_learn_from_arp_request</code>
>> +          is <code>true</code>:
>>          </p>
>>
>>          <pre>
>>  reg9[2] = lookup_arp(inport, arp.spa, arp.sha);
>>  next;
>>          </pre>
>> +
>> +        <p>
>> +          If the option <code>always_learn_from_arp_request</code>
>> +          is <code>false</code>, the above actions will be:
>> +        </p>
>> +
>> +        <pre>
>> +reg9[2] = lookup_arp(inport, arp.spa, arp.sha);
>> +reg9[3] = 1;
>> +next;
>> +        </pre>
>> +
>>        </li>
>>
>>        <li>
>>          <p>
>>            A priority-100 flow which matches on IPv6 Neighbor Discovery
>> -          advertisement packet and applies the actions:
>> +          advertisement packet and applies the actions if the option
>> +          <code>always_learn_from_arp_request</code> is
>> <code>true</code>:
>>          </p>
>>
>>          <pre>
>>  reg9[2] = lookup_nd(inport, nd.target, nd.tll);
>>  next;
>>          </pre>
>> +
>> +        <p>
>> +          If the option <code>always_learn_from_arp_request</code>
>> +          is <code>false</code>, the above actions will be:
>> +        </p>
>> +
>> +        <pre>
>> +reg9[2] = lookup_nd(inport, nd.target, nd.tll);
>> +reg9[3] = 1;
>> +next;
>> +        </pre>
>>        </li>
>>
>>        <li>
>>          <p>
>>            A priority-100 flow which matches on IPv6 Neighbor Discovery
>> -          solicitation packet and applies the actions:
>> +          solicitation packet and applies the actions if the option
>> +          <code>always_learn_from_arp_request</code> is
>> <code>true</code>:
>> +        </p>
>> +
>> +        <pre>
>> +reg9[2] = lookup_nd(inport, ip6.src, nd.sll);
>> +next;
>> +        </pre>
>> +
>> +        <p>
>> +          If the option <code>always_learn_from_arp_request</code>
>> +          is <code>false</code>, the above actions will be:
>>          </p>
>>
>>          <pre>
>>  reg9[2] = lookup_nd(inport, ip6.src, nd.sll);
>> +reg9[3] = lookup_nd_ip(inport, ip6.src);
>>  next;
>>          </pre>
>>        </li>
>> @@ -1604,21 +1672,28 @@ next;
>>
>>      <p>
>>        This table adds flows to learn the mac bindings from the ARP and
>> -      IPv6 Neighbor Solicitation/Advertisement packets if ARP/ND lookup
>> -      failed in the previous table.
>> +      IPv6 Neighbor Solicitation/Advertisement packets if it is needed
>> +      according to the lookup results from the previous stage.
>>      </p>
>>
>>      <p>
>>        reg9[2] will be <code>1</code> if the
>> <code>lookup_arp/lookup_nd</code>
>> -      in the previous table was successful, or if there was no need to
>> do the
>> -      lookup.
>> +      in the previous table was successful or skipped, meaning no need
>> +      to learn mac binding from the packet.
>> +    </p>
>> +
>> +    <p>
>> +      reg9[3] will be <code>1</code> if the
>> +      <code>lookup_arp_ip/lookup_nd_ip</code> in the previous table was
>> +      successful or skipped, meaning it is ok to learn mac binding from
>> +      the packet (if reg9[2] is 0).
>>      </p>
>>
>>      <ul>
>>        <li>
>> -        A priority-100 flow with the match
>> -        <code>reg9[2] == 1</code> and advances the packet
>> -        to the next table as there is no need to learn the neighbor.
>> +        A priority-100 flow with the match <code>reg9[2] == 1 || reg9[3]
>> ==
>> +        0</code> and advances the packet to the next table as there is
>> no need
>> +        to learn the neighbor.
>>        </li>
>>
>>        <li>
>> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
>> index 425f522..b5e7c08 100644
>> --- a/northd/ovn-northd.c
>> +++ b/northd/ovn-northd.c
>> @@ -221,6 +221,7 @@ enum ovn_stage {
>>  /* Register to store the result of check_pkt_larger action. */
>>  #define REGBIT_PKT_LARGER        "reg9[1]"
>>  #define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[2]"
>> +#define REGBIT_LOOKUP_NEIGHBOR_IP_RESULT "reg9[3]"
>>
>>  /* Register to store the eth address associated to a router port for
>> packets
>>   * received in S_ROUTER_IN_ADMISSION.
>> @@ -8242,36 +8243,62 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>           * For ARP packets, table LOOKUP_NEIGHBOR does a lookup for the
>>           * (arp.spa, arp.sha) in the mac binding table using the
>> 'lookup_arp'
>>           * action and stores the result in REGBIT_LOOKUP_NEIGHBOR_RESULT
>> bit.
>> +         * If "always_learn_from_arp_request" is set to false, it will
>> also
>> +         * lookup for the (arp.spa) in the mac binding table using the
>> +         * "lookup_arp_ip" action for ARP request packets, and stores the
>> +         * result in REGBIT_LOOKUP_NEIGHBOR_IP_RESULT bit; or set that
>> bit
>> +         * to "1" directly for ARP response packets.
>>           *
>>           * For IPv6 ND NA packets, table LOOKUP_NEIGHBOR does a lookup
>>           * for the (nd.target, nd.tll) in the mac binding table using the
>>           * 'lookup_nd' action and stores the result in
>> -         * REGBIT_LOOKUP_NEIGHBOR_RESULT bit.
>> +         * REGBIT_LOOKUP_NEIGHBOR_RESULT bit. If
>> +         * "always_learn_from_arp_request" is set to false,
>> +         * REGBIT_LOOKUP_NEIGHBOR_IP_RESULT bit is set.
>>           *
>>           * For IPv6 ND NS packets, table LOOKUP_NEIGHBOR does a lookup
>>           * for the (ip6.src, nd.sll) in the mac binding table using the
>>           * 'lookup_nd' action and stores the result in
>> -         * REGBIT_LOOKUP_NEIGHBOR_RESULT bit.
>> +         * REGBIT_LOOKUP_NEIGHBOR_RESULT bit. If
>> +         * "always_learn_from_arp_request" is set to false, it will also
>> lookup
>> +         * for the (ip6.src) in the mac binding table using the
>> "lookup_nd_ip"
>> +         * action and stores the result in
>> REGBIT_LOOKUP_NEIGHBOR_IP_RESULT
>> +         * bit.
>>           *
>>           * Table LEARN_NEIGHBOR learns the mac-binding using the action
>> -         * - 'put_arp/put_nd' only if REGBIT_LOOKUP_NEIGHBOR_RESULT bit
>> -         * is not set.
>> +         * - 'put_arp/put_nd'. Learning mac-binding is skipped if
>> +         *   REGBIT_LOOKUP_NEIGHBOR_RESULT bit is set or
>> +         *   REGBIT_LOOKUP_NEIGHBOR_IP_RESULT is not set.
>>           *
>>           * */
>>
>>          /* Flows for LOOKUP_NEIGHBOR. */
>> +        bool learn_from_arp_request = smap_get_bool(&od->nbr->options,
>> +            "always_learn_from_arp_request", true);
>> +        ds_clear(&actions);
>> +        ds_put_format(&actions, REGBIT_LOOKUP_NEIGHBOR_RESULT
>> +                      " = lookup_arp(inport, arp.spa, arp.sha); %snext;",
>> +                      learn_from_arp_request ? "" :
>> +                      REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" = 1; ");
>>          ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
>> -                      "arp.op == 2",
>> -                      REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
>> -                      "lookup_arp(inport, arp.spa, arp.sha); next;");
>> +                      "arp.op == 2", ds_cstr(&actions));
>>
>> +        ds_clear(&actions);
>> +        ds_put_format(&actions, REGBIT_LOOKUP_NEIGHBOR_RESULT
>> +                      " = lookup_nd(inport, nd.target, nd.tll); %snext;",
>> +                      learn_from_arp_request ? "" :
>> +                      REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" = 1; ");
>>          ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
>> "nd_na",
>> -                      REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
>> -                      "lookup_nd(inport, nd.target, nd.tll); next;");
>> +                      ds_cstr(&actions));
>>
>> +        ds_clear(&actions);
>> +        ds_put_format(&actions, REGBIT_LOOKUP_NEIGHBOR_RESULT
>> +                      " = lookup_nd(inport, ip6.src, nd.sll); %snext;",
>> +                      learn_from_arp_request ? "" :
>> +                      REGBIT_LOOKUP_NEIGHBOR_IP_RESULT
>> +                      " = lookup_nd_ip(inport, ip6.src); ");
>>          ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
>> "nd_ns",
>> -                      REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
>> -                      "lookup_nd(inport, ip6.src, nd.sll); next;");
>> +                      ds_cstr(&actions));
>>
>>          /* For other packet types, we can skip neighbor learning.
>>           * So set REGBIT_LOOKUP_NEIGHBOR_RESULT to 1. */
>> @@ -8280,8 +8307,12 @@ build_lrouter_flows(struct hmap *datapaths, struct
>> hmap *ports,
>>
>>          /* Flows for LEARN_NEIGHBOR. */
>>          /* Skip Neighbor learning if not required. */
>> +        ds_clear(&match);
>> +        ds_put_format(&match, REGBIT_LOOKUP_NEIGHBOR_RESULT" == 1%s",
>> +                      learn_from_arp_request ? "" :
>> +                      " || "REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" == 0");
>>          ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 100,
>> -                      REGBIT_LOOKUP_NEIGHBOR_RESULT" == 1", "next;");
>> +                      ds_cstr(&match), "next;");
>>
>>          ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
>>                        "arp", "put_arp(inport, arp.spa, arp.sha); next;");
>> @@ -8298,8 +8329,37 @@ build_lrouter_flows(struct hmap *datapaths, struct
>> hmap *ports,
>>              continue;
>>          }
>>
>> +        bool learn_from_arp_request =
>> smap_get_bool(&op->od->nbr->options,
>> +            "always_learn_from_arp_request", true);
>> +
>>          /* Check if we need to learn mac-binding from ARP requests. */
>>          for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
>> +            if (!learn_from_arp_request) {
>> +                /* ARP request to this address should always get learned,
>> +                 * so add a priority-110 flow to set
>> +                 * REGBIT_LOOKUP_NEIGHBOR_IP_RESULT to 1. */
>> +                ds_clear(&match);
>> +                ds_put_format(&match,
>> +                              "inport == %s && arp.spa == %s/%u && "
>> +                              "arp.tpa == %s && arp.op == 1",
>> +                              op->json_key,
>> +                              op->lrp_networks.ipv4_addrs[i].network_s,
>> +                              op->lrp_networks.ipv4_addrs[i].plen,
>> +                              op->lrp_networks.ipv4_addrs[i].addr_s);
>> +                if (op->od->l3dgw_port && op == op->od->l3dgw_port
>> +                    && op->od->l3redirect_port) {
>> +                    ds_put_format(&match, " && is_chassis_resident(%s)",
>> +                                  op->od->l3redirect_port->json_key);
>> +                }
>> +                const char *actions_s = REGBIT_LOOKUP_NEIGHBOR_RESULT
>> +                                  " = lookup_arp(inport, arp.spa,
>> arp.sha); "
>> +                                  REGBIT_LOOKUP_NEIGHBOR_IP_RESULT" = 1;"
>> +                                  " next;";
>> +                ovn_lflow_add_with_hint(lflows, op->od,
>> +                                        S_ROUTER_IN_LOOKUP_NEIGHBOR, 110,
>> +                                        ds_cstr(&match), actions_s,
>> +                                        &op->nbrp->header_);
>> +            }
>>              ds_clear(&match);
>>              ds_put_format(&match,
>>                            "inport == %s && arp.spa == %s/%u && arp.op ==
>> 1",
>> @@ -8311,12 +8371,16 @@ build_lrouter_flows(struct hmap *datapaths,
>> struct hmap *ports,
>>                  ds_put_format(&match, " && is_chassis_resident(%s)",
>>                                op->od->l3redirect_port->json_key);
>>              }
>> +            ds_clear(&actions);
>> +            ds_put_format(&actions, REGBIT_LOOKUP_NEIGHBOR_RESULT
>> +                          " = lookup_arp(inport, arp.spa, arp.sha);
>> %snext;",
>> +                          learn_from_arp_request ? "" :
>> +                          REGBIT_LOOKUP_NEIGHBOR_IP_RESULT
>> +                          " = lookup_arp_ip(inport, arp.spa); ");
>>              ovn_lflow_add_with_hint(lflows, op->od,
>>                                      S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
>> -                                    ds_cstr(&match),
>> -                                    REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
>> -                                    "lookup_arp(inport, arp.spa,
>> arp.sha); "
>> -                                    "next;", &op->nbrp->header_);
>> +                                    ds_cstr(&match), ds_cstr(&actions),
>> +                                    &op->nbrp->header_);
>>          }
>>      }
>>
>> diff --git a/ovn-nb.xml b/ovn-nb.xml
>> index e8450aa..415d30a 100644
>> --- a/ovn-nb.xml
>> +++ b/ovn-nb.xml
>> @@ -1859,6 +1859,33 @@
>>            send traffic between each other.
>>          </p>
>>        </column>
>> +      <column name="options" key="always_learn_from_arp_request"
>> type='{"type": "boolean"}'>
>> +        <p>
>> +          This option controls the behavior when handling IPv4 ARP
>> requests or
>> +          IPv6 ND-NS packets - whether a dynamic neighbor (MAC binding)
>> entry
>> +          is added/updated.
>> +        </p>
>> +
>> +        <p>
>> +          <code>true</code> - Always learn the MAC-IP binding, and
>> add/update
>> +          the MAC binding entry.
>> +        </p>
>> +
>> +        <p>
>> +          <code>false</code> - If there is a MAC binding for that IP and
>> the
>> +          MAC is different, or, if TPA of ARP request belongs to any
>> router
>> +          port on this router, then update/add that MAC-IP binding.
>> Otherwise,
>> +          don't update/add entries.
>> +        </p>
>> +
>> +        <p>
>> +          It is <code>true</code> by default.  It is recommended to set
>> to
>> +          <code>false</code> when a large number of logical routers are
>> +          connected to the same logical switch but most of them never
>> need to
>> +          send traffic between each other, to reduce the size of the MAC
>> +          binding table.
>> +        </p>
>> +      </column>
>>      </group>
>>
>>      <group title="Common Columns">
>> diff --git a/tests/ovn.at b/tests/ovn.at
>> index 67fb754..fc811b4 100644
>> --- a/tests/ovn.at
>> +++ b/tests/ovn.at
>> @@ -4031,6 +4031,18 @@ ip_to_hex() {
>>  sha=f00000000011
>>  spa=`ip_to_hex 192 168 1 100`
>>  tpa=$spa
>> +
>> +# When always_learn_from_arp_request=false, the new mac-binding will not
>> be learned
>> +# through GARP request.
>> +ovn-nbctl --wait=hv set logical_router lr0
>> options:always_learn_from_arp_request=false
>> +
>> +test_arp 11 $sha $spa $tpa
>> +sleep 1
>>
>
> Instead of sleep, I think it's good to run "ovn-nbctl --wait-hv sync"
>

I thought about this, but since mac_binding learning is triggered by the
data plane ARP packets instead of only DB changes, "ovn-nbctl --wait=hv
sync" may still not help in race conditions. So I think sleep is the only
way for now.


>
>
>> +AT_CHECK([ovn-sbctl find mac_binding ip="192.168.1.100"], [0], [])
>> +
>> +# When always_learn_from_arp_request=true, the new mac-binding will be
>> learned.
>> +ovn-nbctl --wait=hv set logical_router lr0
>> options:always_learn_from_arp_request=true
>> +
>>  test_arp 11 $sha $spa $tpa
>>  OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding ip="192.168.1.100" | wc
>> -l` -gt 0])
>>  ovn-nbctl --wait=hv sync
>> @@ -4045,6 +4057,12 @@ test_ip 21 $smac $dmac $sip $dip 11
>>
>>  # lp12 send GARP request to announce ownership of 192.168.1.100.
>>
>> +# Even when always_learn_from_arp_request=false, the existing
>> mac-binding should be
>> +# updated through GARP request.
>> +ovn-nbctl --wait=hv set logical_router lr0
>> options:always_learn_from_arp_request=false
>> +ovn-sbctl lflow-list
>> +as hv2 ovs-ofctl dump-flows br-int
>> +
>>  sha=f00000000012
>>  test_arp 12 $sha $spa $tpa
>>  OVS_WAIT_UNTIL([ovn-sbctl find mac_binding ip="192.168.1.100" | grep
>> f0:00:00:00:00:12])
>> --
>> 2.1.0
>>
>> _______________________________________________
>> 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

Reply via email to