[ovs-dev] [PATCH v3 2/5] ovn: Introduce l3 gateway router.

Darrell Ball dlu998 at gmail.com
Sat May 21 18:48:18 UTC 2016


I made some modifications to code in Patches 1 and 2 to remove the Transit
LS
requirements.

In summary:

I removed all changes to lflow.c thereby reinstating the previous
optimization.

I made some modifications to ovn-northd.c changes to remove the Transit LS
special
aspects and additional arp flows.

I left the other code changes in Patches 1 and 2 as they were.

The overall resulting diff to support both patches 1 and 2 is reduced in
ovn-northd.c
and becomes:

diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index b271f7f..2e2236b 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -702,11 +702,25 @@ ovn_port_update_sbrec(const struct ovn_port *op)
 {
     sbrec_port_binding_set_datapath(op->sb, op->od->sb);
     if (op->nbr) {
-        sbrec_port_binding_set_type(op->sb, "patch");
+        /* If the router is for l3 gateway, it resides on a chassis
+         * and its port type is "gateway". */
+        const char *chassis = smap_get(&op->od->nbr->options, "chassis");
+
+        if (chassis && op->peer && op->peer->od && op->peer->od->nbs){
+            sbrec_port_binding_set_type(op->sb, "gateway");
+        } else {
+            sbrec_port_binding_set_type(op->sb, "patch");
+        }

         const char *peer = op->peer ? op->peer->key : "<error>";
-        const struct smap ids = SMAP_CONST1(&ids, "peer", peer);
-        sbrec_port_binding_set_options(op->sb, &ids);
+        struct smap new;
+        smap_init(&new);
+        smap_add(&new, "peer", peer);
+        if (chassis) {
+            smap_add(&new, "gateway-chassis", chassis);
+        }
+        sbrec_port_binding_set_options(op->sb, &new);
+        smap_destroy(&new);

         sbrec_port_binding_set_parent_port(op->sb, NULL);
         sbrec_port_binding_set_tag(op->sb, NULL, 0);
@@ -716,15 +730,31 @@ ovn_port_update_sbrec(const struct ovn_port *op)
             sbrec_port_binding_set_type(op->sb, op->nbs->type);
             sbrec_port_binding_set_options(op->sb, &op->nbs->options);
         } else {
-            sbrec_port_binding_set_type(op->sb, "patch");
+            const char *chassis = NULL;
+            if (op->peer && op->peer->od && op->peer->od->nbr) {
+                chassis = smap_get(&op->peer->od->nbr->options, "chassis");
+            }
+            /* A switch port connected to a gateway router is also of
+             * type "gateway". */
+            if (chassis) {
+                sbrec_port_binding_set_type(op->sb, "gateway");
+            } else {
+                sbrec_port_binding_set_type(op->sb, "patch");
+            }

             const char *router_port = smap_get(&op->nbs->options,
                                                "router-port");
             if (!router_port) {
                 router_port = "<error>";
             }
-            const struct smap ids = SMAP_CONST1(&ids, "peer", router_port);
-            sbrec_port_binding_set_options(op->sb, &ids);
+            struct smap new;
+            smap_init(&new);
+            smap_add(&new, "peer", router_port);
+            if (chassis) {
+                smap_add(&new, "gateway-chassis", chassis);
+            }
+            sbrec_port_binding_set_options(op->sb, &new);
+            smap_destroy(&new);
         }
         sbrec_port_binding_set_parent_port(op->sb, op->nbs->parent_name);
         sbrec_port_binding_set_tag(op->sb, op->nbs->tag, op->nbs->n_tag);


I added a new test to demonstrate direct DR<->GR connectivity.


AT_SETUP([ovn -- 2 HVs, 3 LRs, 1 DR directly connected to 2 gateway routers
])
AT_KEYWORDS([ovndirectlyconnectedrouters])
AT_SKIP_IF([test $HAVE_PYTHON = no])
ovn_start

# Logical network:
# Three LRs - R1, R2 and R3 that are connected to each other directly
# in 20.0.0.2/31 and 21.0.0.2/31 networks. R1 has switch foo (192.168.1.0/24
)
# connected to it. R2 has alice (172.16.1.0/24) and R3 has bob (10.32.1.0/24
)
# connected to it.

ovn-nbctl create Logical_Router name=R1
ovn-nbctl create Logical_Router name=R2 options:chassis="hv2"
ovn-nbctl create Logical_Router name=R3 options:chassis="hv2"

ovn-nbctl lswitch-add foo
ovn-nbctl lswitch-add alice
ovn-nbctl lswitch-add bob

# Connect foo to R1
ovn-nbctl -- --id=@lrp create Logical_Router_port name=foo \
network=192.168.1.1/24 mac=\"00:00:01:01:02:03\" -- add Logical_Router R1 \
ports @lrp -- lport-add foo rp-foo

ovn-nbctl set Logical_port rp-foo type=router options:router-port=foo \
addresses=\"00:00:01:01:02:03\"

# Connect alice to R2
ovn-nbctl -- --id=@lrp create Logical_Router_port name=alice \
network=172.16.1.1/24 mac=\"00:00:02:01:02:03\" -- add Logical_Router R2 \
ports @lrp -- lport-add alice rp-alice

ovn-nbctl set Logical_port rp-alice type=router options:router-port=alice \
addresses=\"00:00:02:01:02:03\"

# Connect bob to R3
ovn-nbctl -- --id=@lrp create Logical_Router_port name=bob \
network=10.32.1.1/24 mac=\"00:00:03:01:02:03\" -- add Logical_Router R3 \
ports @lrp -- lport-add bob rp-bob

ovn-nbctl set Logical_port rp-bob type=router options:router-port=bob \
addresses=\"00:00:03:01:02:03\"

# Interconnect R1 and R2
lrp1_uuid_2_R2=`ovn-nbctl -- --id=@lrp create Logical_Router_port
name=R1_R2 \
network="20.0.0.2/31" mac=\"00:00:00:02:03:04\" \
-- add Logical_Router R1 ports @lrp`

lrp2_uuid_2_R1=`ovn-nbctl -- --id=@lrp create Logical_Router_port
name=R2_R1 \
network="20.0.0.3/31" mac=\"00:00:00:02:03:05\" \
-- add Logical_Router R2 ports @lrp`

ovn-nbctl set logical_router_port $lrp1_uuid_2_R2 peer="R2_R1"
ovn-nbctl set logical_router_port $lrp2_uuid_2_R1 peer="R1_R2"

# Interconnect R1 and R3
lrp1_uuid_2_R3=`ovn-nbctl -- --id=@lrp create Logical_Router_port
name=R1_R3 \
network="21.0.0.2/31" mac=\"00:00:21:02:03:04\" \
-- add Logical_Router R1 ports @lrp`

lrp3_uuid_2_R1=`ovn-nbctl -- --id=@lrp create Logical_Router_port
name=R3_R1 \
network="21.0.0.3/31" mac=\"00:00:21:02:03:05\" \
-- add Logical_Router R3 ports @lrp`

ovn-nbctl set logical_router_port $lrp1_uuid_2_R3 peer="R3_R1"
ovn-nbctl set logical_router_port $lrp3_uuid_2_R1 peer="R1_R3"

#install static route in R1 to get to alice
ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
ip_prefix=172.16.1.0/24 nexthop=20.0.0.3 -- add Logical_Router \
R1 static_routes @lrt

#install static route in R1 to get to bob
ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
ip_prefix=10.32.1.0/24 nexthop=21.0.0.3 -- add Logical_Router \
R1 static_routes @lrt

#install static route in R2 to get to foo
ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
ip_prefix=192.168.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \
R2 static_routes @lrt

# Create terminal logical ports
# Create logical port foo1 in foo
ovn-nbctl lport-add foo foo1 \
-- lport-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"

# Create logical port alice1 in alice
ovn-nbctl lport-add alice alice1 \
-- lport-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"

# Create logical port bob1 in bob
ovn-nbctl lport-add bob bob1 \
-- lport-set-addresses bob1 "f0:00:00:01:02:05 10.32.1.2"

# Create two hypervisor and create OVS ports corresponding to logical ports.
net_add n1

sim_add hv1
as hv1
ovs-vsctl add-br br-phys
ovn_attach n1 br-phys 192.168.0.1
ovs-vsctl -- add-port br-int hv1-vif1 -- \
    set interface hv1-vif1 external-ids:iface-id=foo1 \
    options:tx_pcap=hv1/vif1-tx.pcap \
    options:rxq_pcap=hv1/vif1-rx.pcap \
    ofport-request=1

sim_add hv2
as hv2
ovs-vsctl add-br br-phys
ovn_attach n1 br-phys 192.168.0.2
ovs-vsctl -- add-port br-int hv2-vif1 -- \
    set interface hv2-vif1 external-ids:iface-id=bob1 \
    options:tx_pcap=hv2/vif1-tx.pcap \
    options:rxq_pcap=hv2/vif1-rx.pcap \
    ofport-request=1

ovs-vsctl -- add-port br-int hv2-vif2 -- \
    set interface hv2-vif2 external-ids:iface-id=alice1 \
    options:tx_pcap=hv2/vif2-tx.pcap \
    options:rxq_pcap=hv2/vif2-rx.pcap \
    ofport-request=2

# Pre-populate the hypervisors' ARP tables so that we don't lose any
# packets for ARP resolution (native tunneling doesn't queue packets
# for ARP resolution).
ovn_populate_arp

# Allow some time for ovn-northd and ovn-controller to catch up.
# XXX This should be more systematic.
sleep 1

ip_to_hex() {
    printf "%02x%02x%02x%02x" "$@"
}
trim_zeros() {
    sed 's/\(00\)\{1,\}$//'
}

# Send ip packets between foo1 and alice1
src_mac="f00000010203"
dst_mac="000001010203"
src_ip=`ip_to_hex 192 168 1 2`
dst_ip=`ip_to_hex 172 16 1 2`
packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
as hv1 ovs-appctl ofproto/trace br-int in_port=1 $packet

# Send ip packets between foo1 and bob1
src_mac="f00000010203"
dst_mac="000001010203"
src_ip=`ip_to_hex 192 168 1 2`
dst_ip=`ip_to_hex 10 32 1 2`
packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet

# Send ip packets from alice1 to foo1
src_mac="f00000010204"
dst_mac="000002010203"
src_ip=`ip_to_hex 172 16 1 2`
dst_ip=`ip_to_hex 192 168 1 2`
packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
as hv2 ovs-appctl netdev-dummy/receive hv2-vif2 $packet

echo "---------NB dump-----"
ovn-nbctl show
echo "---------------------"
ovn-nbctl list logical_router
echo "---------------------"
ovn-nbctl list logical_router_port
echo "---------------------"

echo "---------SB dump-----"
ovn-sbctl list datapath_binding
echo "---------------------"
ovn-sbctl list port_binding
echo "---------------------"
#ovn-sbctl dump-flows
echo "---------------------"

echo "------ hv1 dump ----------"
as hv1 ovs-vsctl show
as hv1 ovs-ofctl show br-int
as hv1 ovs-ofctl dump-flows br-int
echo "------ hv2 dump ----------"
as hv2 ovs-vsctl show
as hv2 ovs-ofctl show br-int
as hv2 ovs-ofctl dump-flows br-int
echo "----------------------------"

# Packet to Expect at alice1
src_mac="000002010203"
dst_mac="f00000010204"
src_ip=`ip_to_hex 192 168 1 2`
dst_ip=`ip_to_hex 172 16 1 2`
expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000

$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif2-tx.pcap | trim_zeros >
received.packets
echo $expected | trim_zeros > expout
AT_CHECK([cat received.packets], [0], [expout])

# Packet to Expect at bob1
src_mac="000003010203"
dst_mac="f00000010205"
src_ip=`ip_to_hex 192 168 1 2`
dst_ip=`ip_to_hex 10 32 1 2`
expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000

$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | trim_zeros >
received1.packets
echo $expected | trim_zeros > expout
AT_CHECK([cat received1.packets], [0], [expout])

# Packet to Expect at foo1
src_mac="000001010203"
dst_mac="f00000010203"
src_ip=`ip_to_hex 172 16 1 2`
dst_ip=`ip_to_hex 192 168 1 2`
expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000

$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | trim_zeros >
received2.packets
echo $expected | trim_zeros > expout
AT_CHECK([cat received2.packets], [0], [expout])

for sim in hv1 hv2; do
    as $sim
    OVS_APP_EXIT_AND_WAIT([ovn-controller])
    OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
    OVS_APP_EXIT_AND_WAIT([ovsdb-server])
done

as ovn-sb
OVS_APP_EXIT_AND_WAIT([ovsdb-server])

as ovn-nb
OVS_APP_EXIT_AND_WAIT([ovsdb-server])

as northd
OVS_APP_EXIT_AND_WAIT([ovn-northd])

as main
OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
OVS_APP_EXIT_AND_WAIT([ovsdb-server])

AT_CLEANUP

On Thu, May 19, 2016 at 1:02 PM, Gurucharan Shetty <guru at ovn.org> wrote:

> Currently OVN has distributed switches and routers. When a packet
> exits a container or a VM, the entire lifecycle of the packet
> through multiple switches and routers are calculated in source
> chassis itself. When the destination endpoint resides on a different
> chassis, the packet is sent to the other chassis and it only goes
> through the egress pipeline of that chassis once and eventually to
> the real destination.
>
> When the packet returns back, the same thing happens. The return
> packet leaves the VM/container on the chassis where it resides.
> The packet goes through all the switches and routers in the logical
> pipleline on that chassis and then sent to the eventual destination
> over the tunnel.
>
> The above makes the logical pipeline very flexible and easy. But,
> creates a problem for cases where you need to add stateful services
> (via conntrack) on switches and routers.
>
> For l3 gateways, we plan to leverage DNAT and SNAT functionality
> and we want to apply DNAT and SNAT rules on a router. So we ideally need
> the packet to go through that router in both directions in the same
> chassis. To achieve this, this commit introduces a new gateway router
> which is
> static and can be connected to your distributed router via a switch.
>
> To make minimal changes in OVN's logical pipeline, this commit
> tries to make the switch port connected to a l3 gateway router look like
> a container/VM endpoint for every other chassis except the chassis
> on which the l3 gateway router resides. On the chassis where the
> gateway router resides, the connection looks just like a patch port.
>
> This is achieved by the doing the following:
> Introduces a new type of port_binding record called 'gateway'.
> On the chassis where the gateway router resides, this port behaves just
> like the port of type 'patch'. The ovn-controller on that chassis
> populates the "chassis" column for this record as an indication for
> other ovn-controllers of its physical location. Other ovn-controllers
> treat this port as they would treat a VM/Container port on a different
> chassis.
>
> Signed-off-by: Gurucharan Shetty <guru at ovn.org>
> ---
>  ovn/controller/binding.c        |   3 +-
>  ovn/controller/ovn-controller.c |   5 +-
>  ovn/controller/patch.c          |  29 ++++++-
>  ovn/controller/patch.h          |   3 +-
>  ovn/northd/ovn-northd.c         |  42 +++++++--
>  ovn/ovn-nb.ovsschema            |   9 +-
>  ovn/ovn-nb.xml                  |  15 ++++
>  ovn/ovn-sb.xml                  |  35 +++++++-
>  tests/ovn.at                    | 184
> ++++++++++++++++++++++++++++++++++++++++
>  9 files changed, 309 insertions(+), 16 deletions(-)
>
> diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
> index a0d8b96..e5e55b1 100644
> --- a/ovn/controller/binding.c
> +++ b/ovn/controller/binding.c
> @@ -200,7 +200,8 @@ binding_run(struct controller_ctx *ctx, const struct
> ovsrec_bridge *br_int,
>                  }
>                  sbrec_port_binding_set_chassis(binding_rec, chassis_rec);
>              }
> -        } else if (chassis_rec && binding_rec->chassis == chassis_rec) {
> +        } else if (chassis_rec && binding_rec->chassis == chassis_rec
> +                   && strcmp(binding_rec->type, "gateway")) {
>              if (ctx->ovnsb_idl_txn) {
>                  VLOG_INFO("Releasing lport %s from this chassis.",
>                            binding_rec->logical_port);
> diff --git a/ovn/controller/ovn-controller.c
> b/ovn/controller/ovn-controller.c
> index 511b184..bc4c24f 100644
> --- a/ovn/controller/ovn-controller.c
> +++ b/ovn/controller/ovn-controller.c
> @@ -364,8 +364,9 @@ main(int argc, char *argv[])
>                      &local_datapaths);
>          }
>
> -        if (br_int) {
> -            patch_run(&ctx, br_int, &local_datapaths, &patched_datapaths);
> +        if (br_int && chassis_id) {
> +            patch_run(&ctx, br_int, chassis_id, &local_datapaths,
> +                      &patched_datapaths);
>
>              struct lport_index lports;
>              struct mcgroup_index mcgroups;
> diff --git a/ovn/controller/patch.c b/ovn/controller/patch.c
> index 4808146..e8abe30 100644
> --- a/ovn/controller/patch.c
> +++ b/ovn/controller/patch.c
> @@ -267,12 +267,28 @@ add_patched_datapath(struct hmap *patched_datapaths,
>  static void
>  add_logical_patch_ports(struct controller_ctx *ctx,
>                          const struct ovsrec_bridge *br_int,
> +                        const char *local_chassis_id,
>                          struct shash *existing_ports,
>                          struct hmap *patched_datapaths)
>  {
> +    const struct sbrec_chassis *chassis_rec;
> +    chassis_rec = get_chassis(ctx->ovnsb_idl, local_chassis_id);
> +    if (!chassis_rec) {
> +        return;
> +    }
> +
>      const struct sbrec_port_binding *binding;
>      SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) {
> -        if (!strcmp(binding->type, "patch")) {
> +        bool local_port = false;
> +        if (!strcmp(binding->type, "gateway")) {
> +            const char *chassis = smap_get(&binding->options,
> +                                           "gateway-chassis");
> +            if (!strcmp(local_chassis_id, chassis)) {
> +                local_port = true;
> +            }
> +        }
> +
> +        if (!strcmp(binding->type, "patch") || local_port) {
>              const char *local = binding->logical_port;
>              const char *peer = smap_get(&binding->options, "peer");
>              if (!peer) {
> @@ -287,13 +303,19 @@ add_logical_patch_ports(struct controller_ctx *ctx,
>              free(dst_name);
>              free(src_name);
>              add_patched_datapath(patched_datapaths, binding);
> +            if (local_port) {
> +                if (binding->chassis != chassis_rec &&
> ctx->ovnsb_idl_txn) {
> +                    sbrec_port_binding_set_chassis(binding, chassis_rec);
> +                }
> +            }
>          }
>      }
>  }
>
>  void
>  patch_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
> -          struct hmap *local_datapaths, struct hmap *patched_datapaths)
> +          const char *chassis_id, struct hmap *local_datapaths,
> +          struct hmap *patched_datapaths)
>  {
>      if (!ctx->ovs_idl_txn) {
>          return;
> @@ -313,7 +335,8 @@ patch_run(struct controller_ctx *ctx, const struct
> ovsrec_bridge *br_int,
>       * 'existing_ports' any patch ports that do exist in the database and
>       * should be there. */
>      add_bridge_mappings(ctx, br_int, &existing_ports, local_datapaths);
> -    add_logical_patch_ports(ctx, br_int, &existing_ports,
> patched_datapaths);
> +    add_logical_patch_ports(ctx, br_int, chassis_id, &existing_ports,
> +                            patched_datapaths);
>
>      /* Now 'existing_ports' only still contains patch ports that exist in
> the
>       * database but shouldn't.  Delete them from the database. */
> diff --git a/ovn/controller/patch.h b/ovn/controller/patch.h
> index d5d842e..7920a48 100644
> --- a/ovn/controller/patch.h
> +++ b/ovn/controller/patch.h
> @@ -27,6 +27,7 @@ struct hmap;
>  struct ovsrec_bridge;
>
>  void patch_run(struct controller_ctx *, const struct ovsrec_bridge
> *br_int,
> -               struct hmap *local_datapaths, struct hmap
> *patched_datapaths);
> +               const char *chassis_id, struct hmap *local_datapaths,
> +               struct hmap *patched_datapaths);
>
>  #endif /* ovn/patch.h */
> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
> index f469e89..7852d83 100644
> --- a/ovn/northd/ovn-northd.c
> +++ b/ovn/northd/ovn-northd.c
> @@ -690,11 +690,24 @@ ovn_port_update_sbrec(const struct ovn_port *op)
>  {
>      sbrec_port_binding_set_datapath(op->sb, op->od->sb);
>      if (op->nbr) {
> -        sbrec_port_binding_set_type(op->sb, "patch");
> +        /* If the router is for l3 gateway, it resides on a chassis
> +         * and its port type is "gateway". */
> +        const char *chassis = smap_get(&op->od->nbr->options, "chassis");
> +        if (chassis) {
> +            sbrec_port_binding_set_type(op->sb, "gateway");
> +        } else {
> +            sbrec_port_binding_set_type(op->sb, "patch");
> +        }
>
>          const char *peer = op->peer ? op->peer->key : "<error>";
> -        const struct smap ids = SMAP_CONST1(&ids, "peer", peer);
> -        sbrec_port_binding_set_options(op->sb, &ids);
> +        struct smap new;
> +        smap_init(&new);
> +        smap_add(&new, "peer", peer);
> +        if (chassis) {
> +            smap_add(&new, "gateway-chassis", chassis);
> +        }
> +        sbrec_port_binding_set_options(op->sb, &new);
> +        smap_destroy(&new);
>
>          sbrec_port_binding_set_parent_port(op->sb, NULL);
>          sbrec_port_binding_set_tag(op->sb, NULL, 0);
> @@ -704,15 +717,32 @@ ovn_port_update_sbrec(const struct ovn_port *op)
>              sbrec_port_binding_set_type(op->sb, op->nbs->type);
>              sbrec_port_binding_set_options(op->sb, &op->nbs->options);
>          } else {
> -            sbrec_port_binding_set_type(op->sb, "patch");
> +            const char *chassis = NULL;
> +            if (op->peer && op->peer->od && op->peer->od->nbr) {
> +                chassis = smap_get(&op->peer->od->nbr->options,
> "chassis");
> +            }
> +
> +            /* A switch port connected to a gateway router is also of
> +             * type "gateway". */
> +            if (chassis) {
> +                sbrec_port_binding_set_type(op->sb, "gateway");
> +            } else {
> +                sbrec_port_binding_set_type(op->sb, "patch");
> +            }
>
>              const char *router_port = smap_get(&op->nbs->options,
>                                                 "router-port");
>              if (!router_port) {
>                  router_port = "<error>";
>              }
> -            const struct smap ids = SMAP_CONST1(&ids, "peer",
> router_port);
> -            sbrec_port_binding_set_options(op->sb, &ids);
> +            struct smap new;
> +            smap_init(&new);
> +            smap_add(&new, "peer", router_port);
> +            if (chassis) {
> +                smap_add(&new, "gateway-chassis", chassis);
> +            }
> +            sbrec_port_binding_set_options(op->sb, &new);
> +            smap_destroy(&new);
>          }
>          sbrec_port_binding_set_parent_port(op->sb, op->nbs->parent_name);
>          sbrec_port_binding_set_tag(op->sb, op->nbs->tag, op->nbs->n_tag);
> diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
> index 8163f6a..fa21b30 100644
> --- a/ovn/ovn-nb.ovsschema
> +++ b/ovn/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
>  {
>      "name": "OVN_Northbound",
> -    "version": "2.1.1",
> -    "cksum": "2615511875 5108",
> +    "version": "2.1.2",
> +    "cksum": "429668869 5325",
>      "tables": {
>          "Logical_Switch": {
>              "columns": {
> @@ -78,6 +78,11 @@
>                                     "max": "unlimited"}},
>                  "default_gw": {"type": {"key": "string", "min": 0, "max":
> 1}},
>                  "enabled": {"type": {"key": "boolean", "min": 0, "max":
> 1}},
> +                "options": {
> +                     "type": {"key": "string",
> +                              "value": "string",
> +                              "min": 0,
> +                              "max": "unlimited"}},
>                  "external_ids": {
>                      "type": {"key": "string", "value": "string",
>                               "min": 0, "max": "unlimited"}}},
> diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
> index d7fd595..d239499 100644
> --- a/ovn/ovn-nb.xml
> +++ b/ovn/ovn-nb.xml
> @@ -630,6 +630,21 @@
>        column is set to <code>false</code>, the router is disabled.  A
> disabled
>        router has all ingress and egress traffic dropped.
>      </column>
> +
> +    <group title="Options">
> +      <p>
> +        Additional options for the logical router.
> +      </p>
> +
> +      <column name="options" key="chassis">
> +        If set, indicates that the logical router in question is
> +        non-distributed and resides in the set chassis. The same
> +        value is also used by <code>ovn-controller</code> to
> +        uniquely identify the chassis in the OVN deployment and
> +        comes from <code>external_ids:system-id</code> in the
> +        <code>Open_vSwitch</code> table of Open_vSwitch database.
> +      </column>
> +    </group>
>
>      <group title="Common Columns">
>        <column name="external_ids">
> diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
> index efd2f9a..741228c 100644
> --- a/ovn/ovn-sb.xml
> +++ b/ovn/ovn-sb.xml
> @@ -1220,7 +1220,12 @@ tcp.flags = RST;
>        which <code>ovn-controller</code>/<code>ovn-controller-vtep</code>
> in
>        turn finds out by monitoring the local hypervisor's Open_vSwitch
>        database, which identifies logical ports via the conventions
> described
> -      in <code>IntegrationGuide.md</code>.
> +      in <code>IntegrationGuide.md</code>. (The exceptions are for
> +      <code>Port_Binding</code> records of <code>type</code> 'gateway',
> +      whose locations are identified by <code>ovn-northd</code> via
> +      the <code>options:gateway-chassis</code> column in this table.
> +      <code>ovn-controller</code> is still responsible to populate the
> +      <code>chassis</code> column.)
>      </p>
>
>      <p>
> @@ -1298,6 +1303,14 @@ tcp.flags = RST;
>              a logical router to a logical switch or to another logical
> router.
>            </dd>
>
> +          <dt><code>gateway</code></dt>
> +          <dd>
> +            One of a pair of logical ports that act as if connected by a
> patch
> +            cable across multiple chassis.  Useful for connecting a
> logical
> +            switch with a gateway router (which is only resident on a
> +            particular chassis).
> +          </dd>
> +
>            <dt><code>localnet</code></dt>
>            <dd>
>              A connection to a locally accessible network from each
> @@ -1336,6 +1349,26 @@ tcp.flags = RST;
>        </column>
>      </group>
>
> +    <group title="Gateway Options">
> +      <p>
> +        These options apply to logical ports with <ref column="type"/> of
> +        <code>gateway</code>.
> +      </p>
> +
> +      <column name="options" key="peer">
> +        The <ref column="logical_port"/> in the <ref
> table="Port_Binding"/>
> +        record for the other side of the 'gateway' port.  The named <ref
> +        column="logical_port"/> must specify this <ref
> column="logical_port"/>
> +        in its own <code>peer</code> option.  That is, the two 'gateway'
> +        logical ports must have reversed <ref column="logical_port"/> and
> +        <code>peer</code> values.
> +      </column>
> +
> +      <column name="options" key="gateway-chassis">
> +        The <code>chassis</code> in which the port resides.
> +      </column>
> +    </group>
> +
>      <group title="Localnet Options">
>        <p>
>          These options apply to logical ports with <ref column="type"/> of
> diff --git a/tests/ovn.at b/tests/ovn.at
> index a827b71..9d93064 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -2848,3 +2848,187 @@ OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
>  OVS_APP_EXIT_AND_WAIT([ovsdb-server])
>
>  AT_CLEANUP
> +
> +
> +AT_SETUP([ovn -- 2 HVs, 2 LRs connected via LS, gateway router])
> +AT_KEYWORDS([ovngatewayrouter])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +ovn_start
> +
> +# Logical network:
> +# Two LRs - R1 and R2 that are connected to each other via LS "join"
> +# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24)
> +# connected to it. R2 has alice (172.16.1.0/24) connected to it.
> +# R2 is a gateway router.
> +
> +
> +
> +# Create two hypervisor and create OVS ports corresponding to logical
> ports.
> +net_add n1
> +
> +sim_add hv1
> +as hv1
> +ovs-vsctl add-br br-phys
> +ovn_attach n1 br-phys 192.168.0.1
> +ovs-vsctl -- add-port br-int hv1-vif1 -- \
> +    set interface hv1-vif1 external-ids:iface-id=foo1 \
> +    options:tx_pcap=hv1/vif1-tx.pcap \
> +    options:rxq_pcap=hv1/vif1-rx.pcap \
> +    ofport-request=1
> +
> +
> +sim_add hv2
> +as hv2
> +ovs-vsctl add-br br-phys
> +ovn_attach n1 br-phys 192.168.0.2
> +ovs-vsctl -- add-port br-int hv2-vif1 -- \
> +    set interface hv2-vif1 external-ids:iface-id=alice1 \
> +    options:tx_pcap=hv2/vif1-tx.pcap \
> +    options:rxq_pcap=hv2/vif1-rx.pcap \
> +    ofport-request=1
> +
> +# Pre-populate the hypervisors' ARP tables so that we don't lose any
> +# packets for ARP resolution (native tunneling doesn't queue packets
> +# for ARP resolution).
> +ovn_populate_arp
> +
> +ovn-nbctl create Logical_Router name=R1
> +ovn-nbctl create Logical_Router name=R2 options:chassis="hv2"
> +
> +ovn-nbctl lswitch-add foo
> +ovn-nbctl lswitch-add alice
> +ovn-nbctl lswitch-add join
> +
> +# Connect foo to R1
> +ovn-nbctl -- --id=@lrp create Logical_Router_port name=foo \
> +network=192.168.1.1/24 mac=\"00:00:01:01:02:03\" -- add Logical_Router
> R1 \
> +ports @lrp -- lport-add foo rp-foo
> +
> +ovn-nbctl set Logical_port rp-foo type=router options:router-port=foo \
> +addresses=\"00:00:01:01:02:03\"
> +
> +# Connect alice to R2
> +ovn-nbctl -- --id=@lrp create Logical_Router_port name=alice \
> +network=172.16.1.1/24 mac=\"00:00:02:01:02:03\" -- add Logical_Router R2
> \
> +ports @lrp -- lport-add alice rp-alice
> +
> +ovn-nbctl set Logical_port rp-alice type=router options:router-port=alice
> \
> +addresses=\"00:00:02:01:02:03\"
> +
> +
> +# Connect R1 to join
> +ovn-nbctl -- --id=@lrp create Logical_Router_port name=R1_join \
> +network=20.0.0.1/24 mac=\"00:00:04:01:02:03\" -- add Logical_Router R1 \
> +ports @lrp -- lport-add join r1-join
> +
> +ovn-nbctl set Logical_port r1-join type=router
> options:router-port=R1_join \
> +addresses='"00:00:04:01:02:03"'
> +
> +# Connect R2 to join
> +ovn-nbctl -- --id=@lrp create Logical_Router_port name=R2_join \
> +network=20.0.0.2/24 mac=\"00:00:04:01:02:04\" -- add Logical_Router R2 \
> +ports @lrp -- lport-add join r2-join
> +
> +ovn-nbctl set Logical_port r2-join type=router
> options:router-port=R2_join \
> +addresses='"00:00:04:01:02:04"'
> +
> +
> +#install static routes
> +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
> +ip_prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \
> +R1 static_routes @lrt
> +
> +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
> +ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \
> +R2 static_routes @lrt
> +
> +# Create logical port foo1 in foo
> +ovn-nbctl lport-add foo foo1 \
> +-- lport-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
> +
> +# Create logical port alice1 in alice
> +ovn-nbctl lport-add alice alice1 \
> +-- lport-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
> +
> +
> +# Allow some time for ovn-northd and ovn-controller to catch up.
> +# XXX This should be more systematic.
> +sleep 2
> +
> +ip_to_hex() {
> +    printf "%02x%02x%02x%02x" "$@"
> +}
> +trim_zeros() {
> +    sed 's/\(00\)\{1,\}$//'
> +}
> +
> +# Send ip packets between foo1 and alice1
> +src_mac="f00000010203"
> +dst_mac="000001010203"
> +src_ip=`ip_to_hex 192 168 1 2`
> +dst_ip=`ip_to_hex 172 16 1 2`
>
> +packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
> +
> +echo "---------NB dump-----"
> +ovn-nbctl show
> +echo "---------------------"
> +ovn-nbctl list logical_router
> +echo "---------------------"
> +ovn-nbctl list logical_router_port
> +echo "---------------------"
> +
> +echo "---------SB dump-----"
> +ovn-sbctl list datapath_binding
> +echo "---------------------"
> +ovn-sbctl list port_binding
> +echo "---------------------"
> +ovn-sbctl dump-flows
> +echo "---------------------"
> +ovn-sbctl list chassis
> +ovn-sbctl list encap
> +echo "---------------------"
> +
> +echo "------ hv1 dump ----------"
> +as hv1 ovs-ofctl show br-int
> +as hv1 ovs-ofctl dump-flows br-int
> +echo "------ hv2 dump ----------"
> +as hv2 ovs-ofctl show br-int
> +as hv2 ovs-ofctl dump-flows br-int
> +echo "----------------------------"
> +
> +# Packet to Expect at alice1
> +src_mac="000002010203"
> +dst_mac="f00000010204"
> +src_ip=`ip_to_hex 192 168 1 2`
> +dst_ip=`ip_to_hex 172 16 1 2`
>
> +expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
> +
> +
> +as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
> +as hv1 ovs-appctl ofproto/trace br-int in_port=1 $packet
> +
> +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap |
> trim_zeros > received1.packets
> +echo $expected | trim_zeros > expout
> +AT_CHECK([cat received1.packets], [0], [expout])
> +
> +for sim in hv1 hv2; do
> +    as $sim
> +    OVS_APP_EXIT_AND_WAIT([ovn-controller])
> +    OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
> +    OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +done
> +
> +as ovn-sb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as ovn-nb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as northd
> +OVS_APP_EXIT_AND_WAIT([ovn-northd])
> +
> +as main
> +OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +AT_CLEANUP
> --
> 1.9.1
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
>



More information about the dev mailing list