[ovs-dev] [PATCH ovn v7 5/6] Support logical switches with multiple localnet ports

Ihar Hrachyshka ihrachys at redhat.com
Tue May 19 14:05:02 UTC 2020


On 5/19/20 8:34 AM, Numan Siddique wrote:
> On Wed, May 13, 2020 at 7:10 PM Ihar Hrachyshka <ihrachys at redhat.com> wrote:
>
>> Assuming only a single localnet port is actually plugged mapped on
>> each chassis, this allows to maintain disjoint networks plugged to the
>> same switch.  This is useful to simplify resource management for
>> OpenStack "routed provider networks" feature [1] where a single
>> "network" (which traditionally maps to logical switches in OVN) is
>> comprised of multiple L2 segments and assumes external L3 routing
>> implemented between the segments.
>>
>> [1]:
>> https://docs.openstack.org/ocata/networking-guide/config-routed-networks.html
>>
>> Signed-off-by: Ihar Hrachyshka <ihrachys at redhat.com>
>>
> Hi Ihar,
>
> Please see below. I've 2 small comments.
>
> With the first comment addressed
> Acked-by: Numan Siddique <numans at ovn.org>
>
> Since Dumitru has already acked your patches, can you please put an
> "Acked-by" tag for Dumitru
> and also mine too when you spin up v8.


Thanks for review!

I'll push v8 once I understand the nature of the first request to change 
(see below).


>
> Thanks
> Numan
>
>
> ---
>>   controller/binding.c   |  16 ++
>>   controller/patch.c     |  14 +-
>>   northd/ovn-northd.c    |  62 +++--
>>   ovn-architecture.7.xml |  50 ++--
>>   ovn-nb.xml             |  23 +-
>>   ovn-sb.xml             |  21 +-
>>   tests/ovn.at           | 504 +++++++++++++++++++++++++++++++++++++++++
>>   7 files changed, 637 insertions(+), 53 deletions(-)
>>
>> diff --git a/controller/binding.c b/controller/binding.c
>> index 9d37a23cc..774c6f242 100644
>> --- a/controller/binding.c
>> +++ b/controller/binding.c
>> @@ -683,12 +683,28 @@ add_localnet_egress_interface_mappings(
>>       }
>>   }
>>
>> +static bool
>> +is_network_plugged(const struct sbrec_port_binding *binding_rec,
>> +                   struct shash *bridge_mappings)
>> +{
>> +    const char *network = smap_get(&binding_rec->options, "network_name");
>> +    if (!network) {
>> +        return false;
>> +    }
>> +    return shash_find_data(bridge_mappings, network);
>>
>
> shash_find_data returns (void *) and not bool.
>
> I'd suggest to have like
>
> return network ?  !!shash_find_data(bridge_mappings, network) : false;


It's not hard to change that, but I'm slightly puzzled and wonder if I 
miss something in the picture that I should grasp before I adjust the 
code without proper understanding. Let me elaborate.

My understanding is that we assume native C99 _Bool in our compiler (not 
a typedef / macro mapped onto int). If that's the case, AFAIU C99 
standard requires implicit conversion of pointer types to booleans, and 
defines it safely (meaning, a NULL-pointer always maps to false 
regardless of actual representation of the NULL-pointer in memory). 
Considering this, your suggestion seems effectively the same as what is 
already written, just more "traditional" (for example, it would be safer 
in C89 environment). Which particular issue do you think we are to 
tackle here?


>
> +}
>> +
>>   static void
>>   consider_localnet_port(const struct sbrec_port_binding *binding_rec,
>>                          struct shash *bridge_mappings,
>>                          struct sset *egress_ifaces,
>>                          struct hmap *local_datapaths)
>>   {
>> +    /* Ignore localnet ports for unplugged networks. */
>> +    if (!is_network_plugged(binding_rec, bridge_mappings)) {
>> +        return;
>> +    }
>> +
>>       add_localnet_egress_interface_mappings(binding_rec,
>>               bridge_mappings, egress_ifaces);
>>
>> diff --git a/controller/patch.c b/controller/patch.c
>> index 349faae17..7ad30d9cc 100644
>> --- a/controller/patch.c
>> +++ b/controller/patch.c
>> @@ -198,8 +198,10 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
>>               continue;
>>           }
>>
>> +        bool is_localnet = false;
>>           const char *patch_port_id;
>>           if (!strcmp(binding->type, "localnet")) {
>> +            is_localnet = true;
>>               patch_port_id = "ovn-localnet-port";
>>           } else if (!strcmp(binding->type, "l2gateway")) {
>>               if (!binding->chassis
>> @@ -224,9 +226,15 @@ add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
>>           struct ovsrec_bridge *br_ln = shash_find_data(&bridge_mappings,
>> network);
>>           if (!br_ln) {
>>               static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>> -            VLOG_ERR_RL(&rl, "bridge not found for %s port '%s' "
>> -                    "with network name '%s'",
>> -                    binding->type, binding->logical_port, network);
>> +            if (!is_localnet) {
>> +                VLOG_ERR_RL(&rl, "bridge not found for %s port '%s' "
>> +                        "with network name '%s'",
>> +                        binding->type, binding->logical_port, network);
>> +            } else {
>> +                VLOG_INFO_RL(&rl, "bridge not found for localnet port
>> '%s' "
>> +                        "with network name '%s'; skipping",
>> +                        binding->logical_port, network);
>> +            }
>>               continue;
>>           }
>>
>> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
>> index 5e7796c5b..2308b10e6 100644
>> --- a/northd/ovn-northd.c
>> +++ b/northd/ovn-northd.c
>> @@ -543,7 +543,9 @@ struct ovn_datapath {
>>       /* The "derived" OVN port representing the instance of l3dgw_port on
>>        * the "redirect-chassis". */
>>       struct ovn_port *l3redirect_port;
>> -    struct ovn_port *localnet_port;
>> +
>> +    struct ovn_port **localnet_ports;
>> +    size_t n_localnet_ports;
>>
>>       struct ovs_list lr_list; /* In list of logical router datapaths. */
>>       /* The logical router group to which this datapath belongs.
>> @@ -611,6 +613,7 @@ ovn_datapath_destroy(struct hmap *datapaths, struct
>> ovn_datapath *od)
>>           ovn_destroy_tnlids(&od->port_tnlids);
>>           bitmap_free(od->ipam_info.allocated_ipv4s);
>>           free(od->router_ports);
>> +        free(od->localnet_ports);
>>           ovn_ls_port_group_destroy(&od->nb_pgs);
>>           destroy_mcast_info_for_datapath(od);
>>
>> @@ -2025,6 +2028,7 @@ join_logical_ports(struct northd_context *ctx,
>>       struct ovn_datapath *od;
>>       HMAP_FOR_EACH (od, key_node, datapaths) {
>>           if (od->nbs) {
>> +            size_t allocated_localnet_ports = 0;
>>
> Small nit:  s/allocated_localnet_ports/n_allocated_localnet_ports
>
>               for (size_t i = 0; i < od->nbs->n_ports; i++) {
>>                   const struct nbrec_logical_switch_port *nbsp
>>                       = od->nbs->ports[i];
>> @@ -2059,7 +2063,12 @@ join_logical_ports(struct northd_context *ctx,
>>                   }
>>
>>                   if (!strcmp(nbsp->type, "localnet")) {
>> -                   od->localnet_port = op;
>> +                   if (od->n_localnet_ports >= allocated_localnet_ports) {
>> +                       od->localnet_ports = x2nrealloc(
>> +                           od->localnet_ports, &allocated_localnet_ports,
>> +                           sizeof *od->localnet_ports);
>> +                   }
>> +                   od->localnet_ports[od->n_localnet_ports++] = op;
>>                   }
>>
>>                   op->lsp_addrs
>> @@ -3018,7 +3027,7 @@ ovn_port_update_sbrec(struct northd_context *ctx,
>>                                 "reside-on-redirect-chassis", false) ||
>>                   op->peer == op->peer->od->l3dgw_port)) {
>>                   add_router_port_garp = true;
>> -            } else if (chassis && op->od->localnet_port) {
>> +            } else if (chassis && op->od->n_localnet_ports) {
>>                   add_router_port_garp = true;
>>               }
>>
>> @@ -4736,8 +4745,8 @@ build_pre_acls(struct ovn_datapath *od, struct hmap
>> *lflows)
>>           for (size_t i = 0; i < od->n_router_ports; i++) {
>>               build_pre_acl_flows(od, od->router_ports[i], lflows);
>>           }
>> -        if (od->localnet_port) {
>> -            build_pre_acl_flows(od, od->localnet_port, lflows);
>> +        for (size_t i = 0; i < od->n_localnet_ports; i++) {
>> +            build_pre_acl_flows(od, od->localnet_ports[i], lflows);
>>           }
>>
>>           /* Ingress and Egress Pre-ACL Table (Priority 110).
>> @@ -6003,9 +6012,9 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset
>> *ips,
>>       /* Send a the packet only to the router pipeline and skip flooding it
>>        * in the broadcast domain (except for the localnet port).
>>        */
>> -    if (od->localnet_port) {
>> +    for (size_t i = 0; i < od->n_localnet_ports; i++) {
>>           ds_put_format(&actions, "clone { outport = %s; output; }; ",
>> -                      od->localnet_port->json_key);
>> +                      od->localnet_ports[i]->json_key);
>>       }
>>       ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
>>       ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
>> @@ -6587,25 +6596,31 @@ build_lswitch_flows(struct hmap *datapaths, struct
>> hmap *ports,
>>           }
>>
>>           bool is_external = lsp_is_external(op->nbsp);
>> -        if (is_external && (!op->od->localnet_port ||
>> +        if (is_external && (!op->od->n_localnet_ports ||
>>                               !op->nbsp->ha_chassis_group)) {
>> -            /* If it's an external port and there is no localnet port
>> +            /* If it's an external port and there are no localnet ports
>>                * and if it doesn't belong to an HA chassis group ignore it.
>> */
>>               continue;
>>           }
>>
>>           for (size_t i = 0; i < op->n_lsp_addrs; i++) {
>> -            const char *json_key;
>>               if (is_external) {
>> -                json_key = op->od->localnet_port->json_key;
>> +                for (size_t j = 0; j < op->od->n_localnet_ports; j++) {
>> +                    build_dhcpv4_options_flows(
>> +                        op, &op->lsp_addrs[i],
>> +                        op->od->localnet_ports[j]->json_key, is_external,
>> +                        lflows);
>> +                    build_dhcpv6_options_flows(
>> +                        op, &op->lsp_addrs[i],
>> +                        op->od->localnet_ports[j]->json_key, is_external,
>> +                        lflows);
>> +                }
>>               } else {
>> -                json_key = op->json_key;
>> +                build_dhcpv4_options_flows(op, &op->lsp_addrs[i],
>> op->json_key,
>> +                                           is_external, lflows);
>> +                build_dhcpv6_options_flows(op, &op->lsp_addrs[i],
>> op->json_key,
>> +                                           is_external, lflows);
>>               }
>> -            build_dhcpv4_options_flows(op, &op->lsp_addrs[i], json_key,
>> -                                       is_external, lflows);
>> -
>> -            build_dhcpv6_options_flows(op, &op->lsp_addrs[i], json_key,
>> -                                       is_external, lflows);
>>           }
>>       }
>>
>> @@ -6661,8 +6676,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
>> hmap *ports,
>>       }
>>
>>       HMAP_FOR_EACH (op, key_node, ports) {
>> -        if (!op->nbsp || !lsp_is_external(op->nbsp) ||
>> -            !op->od->localnet_port) {
>> +        if (!op->nbsp || !lsp_is_external(op->nbsp)) {
>>              continue;
>>           }
>>
>> @@ -6670,8 +6684,10 @@ build_lswitch_flows(struct hmap *datapaths, struct
>> hmap *ports,
>>            * external ports  on chassis not binding those ports.
>>            * This makes the router pipeline to be run only on the chassis
>>            * binding the external ports. */
>> -        build_drop_arp_nd_flows_for_unbound_router_ports(
>> -            op, op->od->localnet_port, lflows);
>> +        for (size_t i = 0; i < op->od->n_localnet_ports; i++) {
>> +            build_drop_arp_nd_flows_for_unbound_router_ports(
>> +                op, op->od->localnet_ports[i], lflows);
>> +        }
>>       }
>>
>>       char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
>> @@ -6889,7 +6905,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
>> hmap *ports,
>>                                 ETH_ADDR_ARGS(mac));
>>                   if (op->peer->od->l3dgw_port
>>                       && op->peer->od->l3redirect_port
>> -                    && op->od->localnet_port) {
>> +                    && op->od->n_localnet_ports) {
>>                       bool add_chassis_resident_check = false;
>>                       if (op->peer == op->peer->od->l3dgw_port) {
>>                           /* The peer of this port represents a distributed
>> @@ -8195,7 +8211,7 @@ build_lrouter_flows(struct hmap *datapaths, struct
>> hmap *ports,
>>                             op->lrp_networks.ipv4_addrs[i].addr_s);
>>
>>               if (op->od->l3dgw_port && op->od->l3redirect_port && op->peer
>> -                && op->peer->od->localnet_port) {
>> +                && op->peer->od->n_localnet_ports) {
>>                   bool add_chassis_resident_check = false;
>>                   if (op == op->od->l3dgw_port) {
>>                       /* Traffic with eth.src =
>> l3dgw_port->lrp_networks.ea_s
>> diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml
>> index 5c125043e..246cebc19 100644
>> --- a/ovn-architecture.7.xml
>> +++ b/ovn-architecture.7.xml
>> @@ -441,9 +441,8 @@
>>
>>     <p>
>>       A <code>localnet</code> logical switch port bridges a logical switch
>> to a
>> -    physical VLAN.  Any given logical switch should have no more than one
>> -    <code>localnet</code> port.  Such a logical switch is used in two
>> -    scenarios:
>> +    physical VLAN.  A logical switch may have one or more
>> <code>localnet</code>
>> +    ports.  Such a logical switch is used in two scenarios:
>>     </p>
>>
>>     <ul>
>> @@ -463,6 +462,31 @@
>>       </li>
>>     </ul>
>>
>> +  <p>
>> +    When a logical switch contains multiple <code>localnet</code> ports,
>> the
>> +    following is assumed.
>> +  </p>
>> +
>> +  <ul>
>> +    <li>
>> +      Each chassis has a bridge mapping for one of the
>> <code>localnet</code>
>> +      physical networks only.
>> +    </li>
>> +
>> +    <li>
>> +      To facilitate interconnectivity between VIF ports of the switch
>> that are
>> +      located on different chassis with different physical network
>> +      connectivity, the fabric implements L3 routing between these
>> adjacent
>> +      physical network segments.
>> +    </li>
>> +  </ul>
>> +
>> +  <p>
>> +    Note: nothing said above implies that a chassis cannot be plugged to
>> +    multiple physical networks as long as they belong to different
>> +    switches.
>> +  </p>
>> +
>>     <p>
>>       A <code>localport</code> logical switch port is a special kind of VIF
>>       logical switch port.  These ports are present in every chassis, not
>> bound
>> @@ -1951,13 +1975,13 @@
>>     <ol>
>>       <li>
>>         The packet first enters the ingress pipeline, and then egress
>> pipeline of
>> -      the source localnet logical switch datapath and is sent out via the
>> +      the source localnet logical switch datapath and is sent out via a
>>         localnet port of the source localnet logical switch (instead of
>> sending
>>         it to router pipeline).
>>       </li>
>>
>>       <li>
>> -      The gateway chassis receives the packet via the localnet port of the
>> +      The gateway chassis receives the packet via a localnet port of the
>>         source localnet logical switch and sends it to the integration
>> bridge.
>>         The packet then enters the ingress pipeline, and then egress
>> pipeline of
>>         the source localnet logical switch datapath and enters the ingress
>> @@ -1972,11 +1996,11 @@
>>         From the router datapath, packet enters the ingress pipeline and
>> then
>>         egress pipeline of the destination localnet logical switch datapath.
>>         It then goes out of the integration bridge to the provider bridge (
>> -      belonging to the destination logical switch) via the localnet port.
>> +      belonging to the destination logical switch) via a localnet port.
>>       </li>
>>
>>       <li>
>> -      The destination chassis receives the packet via the localnet port
>> and
>> +      The destination chassis receives the packet via a localnet port and
>>         sends it to the integration bridge. The packet enters the
>>         ingress pipeline and then egress pipeline of the destination
>> localnet
>>         logical switch and finally delivered to the destination VM port.
>> @@ -1991,13 +2015,13 @@
>>     <ol>
>>       <li>
>>         The packet first enters the ingress pipeline, and then egress
>> pipeline of
>> -      the source localnet logical switch datapath and is sent out via the
>> +      the source localnet logical switch datapath and is sent out via a
>>         localnet port of the source localnet logical switch (instead of
>> sending
>>         it to router pipeline).
>>       </li>
>>
>>       <li>
>> -      The gateway chassis receives the packet via the localnet port of the
>> +      The gateway chassis receives the packet via a localnet port of the
>>         source localnet logical switch and sends it to the integration
>> bridge.
>>         The packet then enters the ingress pipeline, and then egress
>> pipeline of
>>         the source localnet logical switch datapath and enters the ingress
>> @@ -2013,7 +2037,7 @@
>>         egress pipeline of the localnet logical switch datapath which
>> provides
>>         external connectivity. It then goes out of the integration bridge
>> to the
>>         provider bridge (belonging to the logical switch which provides
>> external
>> -      connectivity) via the localnet port.
>> +      connectivity) via a localnet port.
>>       </li>
>>     </ol>
>>
>> @@ -2023,7 +2047,7 @@
>>
>>     <ol>
>>       <li>
>> -      The gateway chassis receives the packet from the localnet port of
>> +      The gateway chassis receives the packet from a localnet port of
>>         the logical switch which provides external connectivity. The packet
>> then
>>         enters the ingress pipeline and then egress pipeline of the localnet
>>         logical switch (which provides external connectivity). The packet
>> then
>> @@ -2034,12 +2058,12 @@
>>         The ingress pipeline of the logical router datapath applies the
>> unNATting
>>         rules. The packet then enters the ingress pipeline and then egress
>>         pipeline of the source localnet logical switch. Since the source VM
>> -      doesn't reside in the gateway chassis, the packet is sent out via
>> the
>> +      doesn't reside in the gateway chassis, the packet is sent out via a
>>         localnet port of the source logical switch.
>>       </li>
>>
>>       <li>
>> -      The source chassis receives the packet via the localnet port and
>> +      The source chassis receives the packet via a localnet port and
>>         sends it to the integration bridge. The packet enters the
>>         ingress pipeline and then egress pipeline of the source localnet
>>         logical switch and finally gets delivered to the source VM port.
>> diff --git a/ovn-nb.xml b/ovn-nb.xml
>> index 95ee4c9e6..acf56486b 100644
>> --- a/ovn-nb.xml
>> +++ b/ovn-nb.xml
>> @@ -244,14 +244,14 @@
>>       <p>
>>         There are two kinds of logical switches, that is, ones that fully
>>         virtualize the network (overlay logical switches) and ones that
>> provide
>> -      simple connectivity to a physical network (bridged logical
>> switches).
>> +      simple connectivity to physical networks (bridged logical switches).
>>         They work in the same way when providing connectivity between
>> logical
>> -      ports on same chasis, but differently when connecting remote logical
>> +      ports on same chassis, but differently when connecting remote
>> logical
>>         ports.  Overlay logical switches connect remote logical ports by
>> tunnels,
>>         while bridged logical switches provide connectivity to remote ports
>> by
>> -      bridging the packets to directly connected physical L2 segment with
>> the
>> +      bridging the packets to directly connected physical L2 segments
>> with the
>>         help of <code>localnet</code> ports.  Each bridged logical switch
>> has
>> -      one and only one <code>localnet</code> port, which has only one
>> special
>> +      one or more <code>localnet</code> ports, which have only one special
>>         address <code>unknown</code>.
>>       </p>
>>
>> @@ -527,10 +527,15 @@
>>
>>             <dt><code>localnet</code></dt>
>>             <dd>
>> -            A connection to a locally accessible network from each
>> -            <code>ovn-controller</code> instance.  A logical switch can
>> only
>> -            have a single <code>localnet</code> port attached.  This is
>> used
>> -            to model direct connectivity to an existing network.
>> +            A connection to a locally accessible network from
>> +            <code>ovn-controller</code> instances that have a
>> corresponding
>> +            bridge mapping.  A logical switch can have multiple
>> +            <code>localnet</code> ports attached.  This type is used to
>> model
>> +            direct connectivity to existing networks.  In this case, each
>> +            chassis should have a mapping for one of the physical networks
>> +            only.  Note: nothing said above implies that a chassis cannot
>> be
>> +            plugged to multiple physical networks as long as they belong
>> to
>> +            different switches.
>>             </dd>
>>
>>             <dt><code>localport</code></dt>
>> @@ -721,7 +726,7 @@
>>             Required.  The name of the network to which the
>> <code>localnet</code>
>>             port is connected.  Each hypervisor, via
>> <code>ovn-controller</code>,
>>             uses its local configuration to determine exactly how to
>> connect to
>> -          this locally accessible network.
>> +          this locally accessible network, if at all.
>>           </column>
>>         </group>
>>
>> diff --git a/ovn-sb.xml b/ovn-sb.xml
>> index 3aa7cd4da..1fa769f3d 100644
>> --- a/ovn-sb.xml
>> +++ b/ovn-sb.xml
>> @@ -2626,10 +2626,15 @@ tcp.flags = RST;
>>
>>             <dt><code>localnet</code></dt>
>>             <dd>
>> -            A connection to a locally accessible network from each
>> -            <code>ovn-controller</code> instance.  A logical switch can
>> only
>> -            have a single <code>localnet</code> port attached.  This is
>> used
>> -            to model direct connectivity to an existing network.
>> +            A connection to a locally accessible network from
>> +            <code>ovn-controller</code> instances that have a
>> corresponding
>> +            bridge mapping.  A logical switch can have multiple
>> +            <code>localnet</code> ports attached.  This type is used to
>> model
>> +            direct connectivity to existing networks.  In this case, each
>> +            chassis should have a mapping for one of the physical networks
>> +            only.  Note: nothing said above implies that a chassis cannot
>> be
>> +            plugged to multiple physical networks as long as they belong
>> to
>> +            different switches.
>>             </dd>
>>
>>             <dt><code>localport</code></dt>
>> @@ -2777,7 +2782,13 @@ tcp.flags = RST;
>>             switch must have a bridge mapping configured to reach that
>>             <code>localnet</code>.  Traffic that arrives on a
>>             <code>localnet</code> port is never forwarded over a tunnel to
>> -          another chassis.
>> +          another chassis.  If there are multiple <code>localnet</code>
>> +          ports in a logical switch, each chassis should only have a
>> single
>> +          bridge mapping for one of the physical networks.  Note: In case
>> of
>> +          multiple <code>localnet</code> ports, to provide
>> interconnectivity
>> +          between all VIFs located on different chassis with different
>> fabric
>> +          connectivity, the fabric should implement some form of routing
>> +          between the segments.
>>           </p>
>>         </column>
>>
>> diff --git a/tests/ovn.at b/tests/ovn.at
>> index 52d994972..0fe3765f8 100644
>> --- a/tests/ovn.at
>> +++ b/tests/ovn.at
>> @@ -48,6 +48,10 @@ m4_define([OVN_CHECK_PACKETS_REMOVE_BROADCAST],
>>     [ovn_check_packets_remove_broadcast__ "$1" "$2"
>>      AT_CHECK([sort $rcv_text], [0], [expout])])
>>
>> +m4_define([OVN_CHECK_PACKETS_CONTAIN],
>> +  [ovn_check_packets__ "$1" "$2"
>> +   AT_CHECK([sort $rcv_text | comm --nocheck-order -2 -3 expout -], [0],
>> [])])
>> +
>>   AT_BANNER([OVN components])
>>
>>   AT_SETUP([ovn -- lexer])
>> @@ -2488,6 +2492,506 @@ OVN_CLEANUP([hv1],[hv2])
>>
>>   AT_CLEANUP
>>
>> +AT_SETUP([ovn -- 2 HVs, 2 LS, routing works for multiple colacated
>> segments attached to different switches])
>> +ovn_start
>> +
>> +for tag in `seq 10 30`; do
>> +    net_add n-$tag
>> +done
>> +
>> +for i in 1 2; do
>> +    sim_add hv-$i
>> +    as hv-$i
>> +    ovs-vsctl add-br br-phys11
>> +    ovs-vsctl add-br br-phys21
>> +    ovs-vsctl set open .
>> external-ids:ovn-bridge-mappings=phys-11:br-phys11,phys-21:br-phys21
>> +    ovn_attach n-11 br-phys11 192.168.0.${i}1
>> +    ovn_attach n-21 br-phys21 192.168.0.${i}2
>> +done
>> +
>> +for i in 1 2; do
>> +    lsname=ls-${i}0
>> +    ovn-nbctl ls-add $lsname
>> +    for tag in `seq ${i}1 ${i}9`; do
>> +        ln_port_name=ln-$tag
>> +        ovn-nbctl lsp-add $lsname $ln_port_name "" $tag
>> +        ovn-nbctl lsp-set-addresses $ln_port_name unknown
>> +        ovn-nbctl lsp-set-type $ln_port_name localnet
>> +        ovn-nbctl lsp-set-options $ln_port_name network_name=phys-$tag
>> +    done
>> +done
>> +
>> +for hv in 1 2; do
>> +    as hv-$hv
>> +    for ls in 1 2; do
>> +        lsp_name=lp-$hv-$ls
>> +        ovs-vsctl add-port br-int vif-$hv-$ls -- \
>> +            set Interface vif-$hv-$ls external-ids:iface-id=$lsp_name \
>> +
>> options:tx_pcap=hv-$hv/vif-$hv-$ls-tx.pcap \
>> +
>> options:rxq_pcap=hv-$hv/vif-$hv-$ls-rx.pcap \
>> +                                  ofport-request=$hv$ls
>> +
>> +        ovn-nbctl lsp-add ls-${ls}0 $lsp_name
>> +        ovn-nbctl lsp-set-addresses $lsp_name f0:00:00:00:00:${hv}${ls}
>> +        ovn-nbctl lsp-set-port-security $lsp_name
>> f0:00:00:00:00:${hv}${ls}
>> +
>> +        OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
>> +    done
>> +done
>> +
>> +
>> +ovn-nbctl --wait=sb sync
>> +ovn-nbctl show
>> +ovn-sbctl dump-flows
>> +
>> +echo "------ OVN dump ------"
>> +ovn-nbctl show
>> +ovn-sbctl show
>> +
>> +for i in 1 2; do
>> +    hv=hv-$i
>> +    echo "------ $hv dump ------"
>> +    as $hv ovs-vsctl show
>> +    as $hv ovs-ofctl -O OpenFlow13 dump-flows br-int
>> +done
>> +
>> +# vif ports
>> +for i in 1-1 1-2 2-1 2-2; do
>> +    : > vif-$i.expected
>> +done
>> +
>> +# localnet ports
>> +for hv in 1 2; do
>> +    : > out-$hv.expected
>> +done
>> +
>> +test_packet() {
>> +    local hv=$1 inport=$2 outport=$3 dst=$4 src=$5 eth=$6 lout=$7
>> +
>> +    : > expout
>> +    if test $lout = unknown; then
>> +        # Expect the packet cloned to all localnet ports
>> +        for tag in `seq ${hv}1 ${hv}9`; do
>> +            echo "output(\"ln-$tag\");" >> expout
>> +        done
>> +    else
>> +        echo "output(\"$lout\");" >> expout
>> +    fi
>> +
>> +    # First try tracing the packet.
>> +    uflow="inport==\"lp-$inport\" && eth.dst==$dst && eth.src==$src &&
>> eth.type==0x$eth"
>> +    AT_CAPTURE_FILE([trace])
>> +    AT_CHECK([ovn-trace --all ls-${hv}0 "$uflow" | tee trace | sed
>> '1,/Minimal trace/d'], [0], [expout])
>> +
>> +    # Then actually send a packet, for an end-to-end test.
>> +    local packet=$(echo $dst$src | sed 's/://g')${eth}
>> +    as hv-$hv ovs-appctl netdev-dummy/receive vif-$inport $packet
>> +
>> +    if test $lout != unknown; then
>> +        # Expect the packet received by the peer VIF port
>> +        echo $packet >> vif-$outport.expected
>> +    fi
>> +
>> +    # regardless, the packet is sent through the bridge
>> +    local packet=$(echo $dst$src | sed 's/://g')810000$(printf "%.2x\n"
>> ${hv}1)${eth}
>> +    echo $packet >> out-$hv.expected
>> +}
>> +
>> +test_packet 1 1-1 2-1 f0:00:00:00:00:21 f0:00:00:00:00:11 1001 lp-2-1
>> +test_packet 2 2-2 1-2 f0:00:00:00:00:12 f0:00:00:00:00:22 1001 lp-1-2
>> +
>> +# unknown mac goes through localnet port
>> +test_packet 1 1-1 2-1 f0:00:00:00:00:e0 f0:00:00:00:00:11 1001 unknown
>> +test_packet 2 2-2 1-2 f0:00:00:00:00:e0 f0:00:00:00:00:22 1001 unknown
>> +
>> +# Now check the packets actually received against the ones expected.
>> +for hv in 1 2; do
>> +    for ls in 1 2; do
>> +        port=$hv-$ls
>> +        # check that packets targeted to actual vifs arrived on the other
>> end
>> +        OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv-$hv/vif-$port-tx.pcap],
>> [vif-$port.expected])
>> +    done
>> +    # check that all packets, whether to known or unknown mac addresses,
>> were sent to fabric
>> +
>> OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv-$hv/br-phys${hv}1_n-${hv}1-tx.pcap],
>> [out-$hv.expected])
>> +done
>> +
>> +OVN_CLEANUP([hv-1],[hv-2])
>> +
>> +AT_CLEANUP
>> +
>> +AT_SETUP([ovn -- 2 HVs, 2 LS, broadcast traffic with multiple localnet
>> ports per switch])
>> +ovn_start
>> +
>> +for tag in `seq 10 30`; do
>> +    net_add n-$tag
>> +done
>> +
>> +for i in 1 2; do
>> +    sim_add hv-$i
>> +    as hv-$i
>> +    ovs-vsctl add-br br-phys11
>> +    ovs-vsctl add-br br-phys21
>> +    ovs-vsctl set open .
>> external-ids:ovn-bridge-mappings=phys-11:br-phys11,phys-21:br-phys21
>> +    ovn_attach n-11 br-phys11 192.168.0.${i}1
>> +    ovn_attach n-21 br-phys21 192.168.0.${i}2
>> +done
>> +
>> +for i in 1 2; do
>> +    lsname=ls-${i}0
>> +    ovn-nbctl ls-add $lsname
>> +    for tag in `seq ${i}1 ${i}9`; do
>> +        ln_port_name=ln-$tag
>> +        ovn-nbctl lsp-add $lsname $ln_port_name "" $tag
>> +        ovn-nbctl lsp-set-addresses $ln_port_name unknown
>> +        ovn-nbctl lsp-set-type $ln_port_name localnet
>> +        ovn-nbctl lsp-set-options $ln_port_name network_name=phys-$tag
>> +    done
>> +done
>> +
>> +for hv in 1 2; do
>> +    as hv-$hv
>> +    for ls in 1 2; do
>> +        for peer in 8 9; do
>> +            lsp_name=lp-$hv-$ls-$peer
>> +            ovs-vsctl add-port br-int vif-$hv-$ls-$peer -- \
>> +                set Interface vif-$hv-$ls-$peer
>> external-ids:iface-id=$lsp_name \
>> +
>> options:tx_pcap=hv-$hv/vif-$hv-$ls-$peer-tx.pcap \
>> +
>> options:rxq_pcap=hv-$hv/vif-$hv-$ls-$peer-rx.pcap \
>> +                                      ofport-request=$hv$ls$peer
>> +
>> +            ovn-nbctl lsp-add ls-${ls}0 $lsp_name
>> +            ovn-nbctl lsp-set-addresses $lsp_name
>> f0:00:00:00:0${peer}:${hv}${ls}
>> +            ovn-nbctl lsp-set-port-security $lsp_name
>> f0:00:00:00:0${peer}:${hv}${ls}
>> +
>> +            OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
>> +
>> +            : > vif-$hv-$ls-$peer.expected
>> +        done
>> +    done
>> +done
>> +
>> +
>> +ovn-nbctl --wait=sb sync
>> +ovn-nbctl show
>> +ovn-sbctl dump-flows
>> +
>> +echo "------ OVN dump ------"
>> +ovn-nbctl show
>> +ovn-sbctl show
>> +
>> +for i in 1 2; do
>> +    hv=hv-$i
>> +    echo "------ $hv dump ------"
>> +    as $hv ovs-vsctl show
>> +    as $hv ovs-ofctl -O OpenFlow13 dump-flows br-int
>> +done
>> +
>> +# localnet ports
>> +for hv in 1 2; do
>> +    : > out-$hv.expected
>> +done
>> +
>> +test_packet() {
>> +    local hv=$1 inport=$2 dst=$3 src=$4 eth=$5
>> +    shift; shift; shift; shift; shift
>> +
>> +    : > expout
>> +    for lout in "$@"; do
>> +        if test $lout = unknown; then
>> +            # Expect the packet cloned to all localnet ports
>> +            for tag in `seq ${hv}1 ${hv}9`; do
>> +                echo "output(\"ln-$tag\");" >> expout
>> +            done
>> +        else
>> +            echo "output(\"$lout\");" >> expout
>> +        fi
>> +    done
>> +
>> +    # First try tracing the packet.
>> +    uflow="inport==\"lp-$inport\" && eth.dst==$dst && eth.src==$src &&
>> eth.type==0x$eth"
>> +    AT_CAPTURE_FILE([trace])
>> +    AT_CHECK([ovn-trace --all ls-${hv}0 "$uflow" | tee trace | sed
>> '1,/Minimal trace/d' | sort], [0], [expout])
>> +
>> +    # Then actually send a packet, for an end-to-end test.
>> +    local packet=$(echo $dst$src | sed 's/://g')${eth}
>> +    as hv-$hv ovs-appctl netdev-dummy/receive vif-$inport $packet
>> +
>> +    for lout in "$@"; do
>> +        if test $lout != unknown; then
>> +            # Expect the packet received by the peer VIF port
>> +            echo $packet >> vif-${lout#lp-}.expected
>> +        fi
>> +    done
>> +
>> +    # regardless, the packet is sent through the bridge
>> +    local packet=$(echo $dst$src | sed 's/://g')810000$(printf "%.2x\n"
>> ${hv}1)${eth}
>> +    echo $packet >> out-$hv.expected
>> +}
>> +
>> +test_packet 1 1-1-8 f0:00:00:00:08:21 f0:00:00:00:08:11 1001 lp-2-1-8
>> +test_packet 2 2-2-8 f0:00:00:00:08:12 f0:00:00:00:08:22 1001 lp-1-2-8
>> +
>> +# unknown mac goes through localnet port
>> +test_packet 1 1-1-8 f0:00:00:00:08:e0 f0:00:00:00:08:11 1001 unknown
>> +test_packet 2 2-2-8 f0:00:00:00:08:e0 f0:00:00:00:08:22 1001 unknown
>> +
>> +# broadcast traffic goes to all peers, foreign and local
>> +test_packet 1 1-1-8 ff:ff:ff:ff:ff:ff f0:00:00:00:08:11 1001 $(for n in
>> `seq 11 19`; do echo ln-$n; done) lp-1-1-9 lp-2-1-8 lp-2-1-9
>> +
>> +# Now check the packets actually received against the ones expected.
>> +for hv in 1 2; do
>> +    for ls in 1 2; do
>> +        for peer in 8 9; do
>> +            port=$hv-$ls-$peer
>> +            # check that packets targeted to actual vifs arrived on the
>> other end
>> +            OVN_CHECK_PACKETS_CONTAIN([hv-$hv/vif-$port-tx.pcap],
>> [vif-$port.expected])
>> +        done
>> +    done
>> +    # check that all packets, whether to known or unknown mac addresses,
>> were sent to fabric
>> +    OVN_CHECK_PACKETS_CONTAIN([hv-$hv/br-phys${hv}1_n-${hv}1-tx.pcap],
>> [out-$hv.expected])
>> +done
>> +
>> +OVN_CLEANUP([hv-1],[hv-2])
>> +
>> +AT_CLEANUP
>> +
>> +AT_SETUP([ovn -- 2 HVs, 2 LS, switching between multiple localnet ports
>> with same tags])
>> +ovn_start
>> +
>> +# In this test case we create two switches with multiple localnet ports.
>> Only a
>> +# single localnet of the same tag is connected to fabric for each switch.
>> Two
>> +# hypervisors have VIFs that belong to these switches. The test validates
>> that
>> +# routing between these switches and hypervisors still works regardless
>> of the
>> +# number of (unplugged) localnet ports.
>> +
>> +# two switches, each connected to lots of networks
>> +for i in 1 2; do
>> +    ovn-nbctl ls-add ls-$i
>> +    for tag in `seq 10 20`; do
>> +        ln_port_name=ln-$i-$tag
>> +        ovn-nbctl lsp-add ls-$i $ln_port_name "" $tag
>> +        ovn-nbctl lsp-set-addresses $ln_port_name unknown
>> +        ovn-nbctl lsp-set-type $ln_port_name localnet
>> +        ovn-nbctl lsp-set-options $ln_port_name network_name=phys-$tag
>> +    done
>> +done
>> +
>> +# multiple networks
>> +for tag in `seq 10 20`; do
>> +    net_add n-$tag
>> +done
>> +
>> +# two hypervisors, each connected to the same network
>> +for i in 1 2; do
>> +    sim_add hv-$i
>> +    as hv-$i
>> +    ovs-vsctl add-br br-phys
>> +    ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys-20:br-phys
>> +    ovn_attach n-10 br-phys 192.168.0.$i
>> +done
>> +
>> +# two vif ports, one per switch
>> +for i in 1 2; do
>> +    as hv-$i
>> +    ovs-vsctl add-port br-int vif-$i -- \
>> +        set Interface vif-$i external-ids:iface-id=lp-$i \
>> +                              options:tx_pcap=hv-$i/vif-$i-tx.pcap \
>> +                              options:rxq_pcap=hv-$i/vif-$i-rx.pcap \
>> +                              ofport-request=$i
>> +
>> +    lsp_name=lp-$i
>> +    ovn-nbctl lsp-add ls-$i $lsp_name
>> +    ovn-nbctl lsp-set-addresses $lsp_name f0:00:00:00:00:0$i
>> +    ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:0$i
>> +
>> +    OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
>> +done
>> +
>> +ovn-nbctl --wait=sb sync
>> +ovn-nbctl show
>> +ovn-sbctl dump-flows
>> +
>> +# vif ports
>> +for i in 1 2; do
>> +    : > vif-$i.expected
>> +done
>> +
>> +# localnet ports
>> +for i in 1 2; do
>> +    for tag in `seq 10 20`; do
>> +        : > $i-$tag.expected
>> +    done
>> +done
>> +
>> +test_packet() {
>> +    local inport=$1 outport=$2 dst=$3 src=$4 eth=$5 eout=$6 lout=$7
>> +
>> +    # Expect the packet cloned to all localnet ports
>> +    : > expout
>> +    for tag in `seq 10 20`; do
>> +        echo "output(\"ln-$inport-$tag\");" >> expout
>> +    done
>> +
>> +    # First try tracing the packet.
>> +    uflow="inport==\"lp-$inport\" && eth.dst==$dst && eth.src==$src &&
>> eth.type==0x$eth"
>> +    AT_CAPTURE_FILE([trace])
>> +    AT_CHECK([ovn-trace --all ls-$inport "$uflow" | tee trace | sed
>> '1,/Minimal trace/d'], [0], [expout])
>> +
>> +    # Then actually send a packet, for an end-to-end test.
>> +    local packet=$(echo $dst$src | sed 's/://g')${eth}
>> +    as hv-$1 ovs-appctl netdev-dummy/receive vif-$inport $packet
>> +
>> +    # Expect the packet received by the peer VIF port
>> +    echo $packet >> vif-$outport.expected
>> +
>> +    # Expect the packet to transfer through the common fabric network
>> +    local packet=$(echo $dst$src | sed 's/://g')810000$(printf "%.2x"
>> 20)${eth}
>> +    echo $packet >> $1-10.expected
>> +}
>> +
>> +test_packet 1 2 f0:00:00:00:00:02 f0:00:00:00:00:01 1001 ln-1-10 ln-1-10
>> +test_packet 1 2 f0:00:00:00:00:02 f0:00:00:00:00:01 1002 ln-1-10 ln-1-10
>> +
>> +test_packet 2 1 f0:00:00:00:00:01 f0:00:00:00:00:02 1003 ln-2-10 ln-2-10
>> +test_packet 2 1 f0:00:00:00:00:01 f0:00:00:00:00:02 1004 ln-2-10 ln-2-10
>> +
>> +# Dump a bunch of info helpful for debugging if there's a failure.
>> +
>> +echo "------ OVN dump ------"
>> +ovn-nbctl show
>> +ovn-sbctl show
>> +
>> +for i in 1 2; do
>> +    hv=hv-$i
>> +    echo "------ $hv dump ------"
>> +    as $hv ovs-vsctl show
>> +    as $hv ovs-ofctl -O OpenFlow13 dump-flows br-int
>> +done
>> +
>> +# Now check the packets actually received against the ones expected.
>> +for i in 1 2; do
>> +    OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv-$i/vif-$i-tx.pcap],
>> [vif-$i.expected])
>> +    OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv-$i/br-phys_n-10-tx.pcap],
>> [$i-10.expected])
>> +done
>> +
>> +OVN_CLEANUP([hv-1],[hv-2])
>> +
>> +AT_CLEANUP
>> +
>> +AT_SETUP([ovn -- 2 HVs, 1 LS, no switching between multiple localnet
>> ports with different tags])
>> +ovn_start
>> +
>> +# In this test case we create a single switch connected to two physical
>> +# networks via two localnet ports. Then we create two hypervisors, with 2
>> +# ports on each. The test validates no interconnectivity between VIF ports
>> +# located on chassis plugged to different physical networks.
>> +
>> +# create the single switch with two locanet ports
>> +ovn-nbctl ls-add ls1
>> +for tag in 10 20; do
>> +    ln_port_name=ln-$tag
>> +    ovn-nbctl lsp-add ls1 $ln_port_name "" $tag
>> +    ovn-nbctl lsp-set-addresses $ln_port_name unknown
>> +    ovn-nbctl lsp-set-type $ln_port_name localnet
>> +    ovn-nbctl lsp-set-options $ln_port_name network_name=phys-$tag
>> +done
>> +
>> +# create fabric networks
>> +for tag in 10 20; do
>> +    net_add n-$tag
>> +done
>> +
>> +# create four chassis, each connected to one network, each with a single
>> VIF port
>> +for tag in 10 20; do
>> +    for i in 1 2; do
>> +        sim_add hv-$tag-$i
>> +        as hv-$tag-$i
>> +        ovs-vsctl add-br br-phys
>> +        ovs-vsctl set open .
>> external-ids:ovn-bridge-mappings=phys-$tag:br-phys
>> +        ovn_attach n-$tag br-phys 192.168.$i.$tag
>> +
>> +        ovs-vsctl add-port br-int vif-$tag-$i -- \
>> +            set Interface vif-$tag-$i external-ids:iface-id=lp-$tag-$i \
>> +
>> options:tx_pcap=hv-$tag-$i/vif-$tag-$i-tx.pcap \
>> +
>> options:rxq_pcap=hv-$tag-$i/vif-$tag-$i-rx.pcap \
>> +                                  ofport-request=$tag$i
>> +
>> +        lsp_name=lp-$tag-$i
>> +        ovn-nbctl lsp-add ls1 $lsp_name
>> +        ovn-nbctl lsp-set-addresses $lsp_name f0:00:00:00:0$i:$tag
>> +        ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:0$i:$tag
>> +
>> +        OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
>> +    done
>> +done
>> +ovn-nbctl --wait=sb sync
>> +ovn-sbctl dump-flows
>> +
>> +for tag in 10 20; do
>> +    for i in 1 2; do
>> +        : > $tag-$i.expected
>> +    done
>> +done
>> +
>> +vif_to_hv() {
>> +    echo hv-$1
>> +}
>> +
>> +test_packet() {
>> +    local inport=$1 dst=$2 src=$3 eth=$4 eout=$5 lout=$6
>> +
>> +    # First try tracing the packet.
>> +    uflow="inport==\"lp-$inport\" && eth.dst==$dst && eth.src==$src &&
>> eth.type==0x$eth"
>> +    echo "output(\"$lout\");" > expout
>> +    AT_CAPTURE_FILE([trace])
>> +    AT_CHECK([ovn-trace --all ls1 "$uflow" | tee trace | sed '1,/Minimal
>> trace/d'], [0], [expout])
>> +
>> +    # Then actually send a packet, for an end-to-end test.
>> +    local packet=$(echo $dst$src | sed 's/://g')${eth}
>> +    hv=`vif_to_hv $inport`
>> +    vif=vif-$inport
>> +    as $hv ovs-appctl netdev-dummy/receive $vif $packet
>> +    if test $eth = 1002 -o $eth = 2002; then
>> +        echo $packet >> ${eout#lp-}.expected
>> +    fi
>> +}
>> +
>> +# different fabric networks -> should fail
>> +test_packet 10-1 f0:00:00:00:01:20 f0:00:00:00:01:10 1001 lp-20-1 lp-20-1
>> +test_packet 20-1 f0:00:00:00:01:10 f0:00:00:00:01:20 2001 lp-10-1 lp-10-1
>> +
>> +# same fabric networks -> should pass
>> +test_packet 10-1 f0:00:00:00:02:10 f0:00:00:00:01:10 1002 lp-10-2 lp-10-2
>> +test_packet 20-1 f0:00:00:00:02:20 f0:00:00:00:01:20 2002 lp-20-2 lp-20-2
>> +test_packet 10-2 f0:00:00:00:01:10 f0:00:00:00:02:10 1002 lp-10-1 lp-10-1
>> +test_packet 20-2 f0:00:00:00:01:20 f0:00:00:00:02:20 2002 lp-20-1 lp-20-1
>> +
>> +# Dump a bunch of info helpful for debugging if there's a failure.
>> +echo "------ OVN dump ------"
>> +ovn-nbctl show
>> +ovn-sbctl show
>> +
>> +for tag in 10 20; do
>> +    for i in 1 2; do
>> +        hv=hv-$tag-$i
>> +        echo "------ $hv dump ------"
>> +        as $hv ovs-vsctl show
>> +        as $hv ovs-ofctl -O OpenFlow13 dump-flows br-int
>> +    done
>> +done
>> +
>> +# Now check the packets actually received against the ones expected.
>> +for tag in 10 20; do
>> +    for i in 1 2; do
>> +        echo "hv = $tag-$i"
>> +
>> OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv-$tag-$i/vif-$tag-$i-tx.pcap],
>> [$tag-$i.expected])
>> +    done
>> +done
>> +
>> +OVN_CLEANUP([hv-10-1],[hv-10-2],[hv-20-1],[hv-20-2])
>> +
>> +AT_CLEANUP
>> +
>>   AT_SETUP([ovn -- vtep: 3 HVs, 1 VIFs/HV, 1 GW, 1 LS])
>>   AT_KEYWORDS([vtep])
>>   ovn_start
>> --
>> 2.26.2
>>
>> _______________________________________________
>> dev mailing list
>> dev at openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>>




More information about the dev mailing list