On Wed, May 13, 2020 at 7:10 PM Ihar Hrachyshka <[email protected]> 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 <[email protected]>
>

Hi Ihar,

Please see below. I've 2 small comments.

With the first comment addressed
Acked-by: Numan Siddique <[email protected]>

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
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;


+}
> +
>  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
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to