From: Venkata Anil <vkomm...@redhat.com>

When a vm on a vlan tenant network sends traffic to an external network,
it is tunneled from host chassis to gateway chassis. In the earlier
discussion [1], Russel (also in his doc [2]) suggested if we can figure
out a way for OVN to do this redirect to the gateway host over a VLAN
network. This patch implements his suggestion i.e will redirect to
gateway chassis using incoming tenant vlan network. Gateway chassis are
expected to be configured with tenant vlan networks. In this approach,
new logical and physical flows introduced for packet processing in both
host and gateway chassis.

Packet processing in the host chassis:
1) Packet enters into router pipeline from switch pipeline
2) A new flow added in lr_in_ip_routing, for a packet coming from vlan
   tenant network and destined for external network, which sets
   REGBIT_NAT_REDIRECT i.e
   table=7 (lr_in_ip_routing   ), priority=1    , match=(inport ==
   "lrp-44383893-613a-4bfe-b483-e7d0dc3055cd" && !is_chassis_resident("
   cr-lrp-a6e3d2ab-313a-4ea3-8ec4-c3c774a11f49")), action=(reg9[0] = 1;
   next;)
   When REGBIT_NAT_REDIRECT set,
   a) lr_in_arp_resolve, will set packet eth.dst to distibuted gateway
      port MAC
   b) lr_in_gw_redirect, will set chassisredirect port as outport
3) A new ovs flow added in physical table 32 will use source vlan tenant
   network tag as vlan ID for sending the packet to gateway chassis.
   As this vlan packet destination MAC is distibuted gateway port MAC,
   packet will only reach the gateway chassis.
   table=32,priority=150,reg14=0x3,reg15=0x6,metadata=0x4
   actions=mod_vlan_vid:2010,output:25,strip_vlan

Packet processing in the gateway chassis:
1) A new ovs flow added in physical table 0 to pass vlan traffic coming
   from localnet port to the connected router pipeline(i.e router
   attached to vlan tenant network).
   This flow will set router metadata, reg14 to router's patch port(lrp)
   (i.e patch port connecting router and vlan tenant network) and a new
   MLF_RCV_FROM_VLAN flag.
   table=0,priority=150,in_port=67,dl_vlan=2010 actions=strip_vlan,
   load:0x4->OXM_OF_METADATA[],load:0x3->NXM_NX_REG14[],
   load:0x1->NXM_NX_REG10[5],resubmit(,8)
2) A new flow added in lr_in_admission which checks MLF_RCV_FROM_VLAN
   and allows the packet.
   table=0 (lr_in_admission    ), priority=100  , match=(
   flags.rcv_from_vlan == 1 && inport == "lrp-44383893-613a-4bfe-b483-
   e7d0dc3055cd" && is_chassis_resident("cr-lrp-a6e3d2ab-313a-4ea3-
   8ec4-c3c774a11f49")), action=(next;)
   Then packet will pass through router ingress and egress pipelines and
   then to external switch pipeline.

In a scenario where the traffic between two vms in the same tenant vlan
network across different chassis i.e if "vm1" on tenant vlan network
"net1" is on host chassis "ch1" and "vm2" on same tenant vlan network
"net1" is on gateway chassis "gw1". When the packet arrived on "gw1"
chassis from localnet port, we still send it to router pipeline and router
pipeline will send it to destination switch ("net1") pipeline.
But in this case when packet arrives at "vm2", it will have router MAC as
source MAC as the packet is routed in gateway chassis.

[1] https://mail.openvswitch.org/pipermail/ovs-discuss/2018-April/046557.html
[2] Point 3 in section 3.3.1 - Future Enhancements
https://docs.google.com/document/d/1JecGIXPH0RAqfGvD0nmtBdEU1zflHACp8WSRnKCFSgg/edit#

Reported-at: 
https://mail.openvswitch.org/pipermail/ovs-discuss/2018-April/046543.html

Signed-off-by: Venkata Anil <vkomm...@redhat.com>
---
 ovn/controller/bfd.c            |  3 +-
 ovn/controller/binding.c        | 10 ++++-
 ovn/controller/ovn-controller.c |  3 ++
 ovn/controller/ovn-controller.h | 16 +++++++-
 ovn/controller/physical.c       | 83 +++++++++++++++++++++++++++++++++++++++++
 ovn/lib/logical-fields.c        |  4 ++
 ovn/lib/logical-fields.h        |  2 +
 ovn/northd/ovn-northd.c         | 32 ++++++++++++++++
 8 files changed, 150 insertions(+), 3 deletions(-)

diff --git a/ovn/controller/bfd.c b/ovn/controller/bfd.c
index 8f020d5..cbbd3ba 100644
--- a/ovn/controller/bfd.c
+++ b/ovn/controller/bfd.c
@@ -139,8 +139,9 @@ bfd_travel_gw_related_chassis(struct local_datapath *dp,
                                   struct local_datapath_node, node);
         dp = dp_binding->dp;
         free(dp_binding);
+        const struct sbrec_datapath_binding *pdp;
         for (size_t i = 0; i < dp->n_peer_dps; i++) {
-            const struct sbrec_datapath_binding *pdp = dp->peer_dps[i];
+            pdp = dp->peer_dps[i]->peer_dp;
             if (!pdp) {
                 continue;
             }
diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
index 0785a94..f02bde5 100644
--- a/ovn/controller/binding.c
+++ b/ovn/controller/binding.c
@@ -148,10 +148,14 @@ add_local_datapath__(struct controller_ctx *ctx,
                                 "lport-by-datapath", &cursor);
 
     SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, &cursor, lpval) {
+        if (!strcmp(pb->type, "chassisredirect")) {
+            ld->chassisredirect_port = pb;
+        }
         if (!strcmp(pb->type, "patch")) {
             const char *peer_name = smap_get(&pb->options, "peer");
             if (peer_name) {
                 const struct sbrec_port_binding *peer;
+                struct peer_datapath *pdp;
 
                 peer = lport_lookup_by_name( ctx->ovnsb_idl, peer_name);
 
@@ -162,8 +166,12 @@ add_local_datapath__(struct controller_ctx *ctx,
                     ld->peer_dps = xrealloc(
                             ld->peer_dps,
                             ld->n_peer_dps * sizeof *ld->peer_dps);
-                    ld->peer_dps[ld->n_peer_dps - 1] = datapath_lookup_by_key(
+                    pdp = xcalloc(1, sizeof(struct peer_datapath));
+                    pdp->peer_dp = datapath_lookup_by_key(
                         ctx->ovnsb_idl, peer->datapath->tunnel_key);
+                    pdp->patch = pb;
+                    pdp->peer = peer;
+                    ld->peer_dps[ld->n_peer_dps - 1] = pdp;
                 }
             }
         }
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 86e1836..55573fd 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -803,6 +803,9 @@ main(int argc, char *argv[])
 
         struct local_datapath *cur_node, *next_node;
         HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, &local_datapaths) {
+            for (int i = 0; i < cur_node->n_peer_dps; i++) {
+                free(cur_node->peer_dps[i]);
+            }
             free(cur_node->peer_dps);
             hmap_remove(&local_datapaths, &cur_node->hmap_node);
             free(cur_node);
diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h
index 6617b0c..8023de2 100644
--- a/ovn/controller/ovn-controller.h
+++ b/ovn/controller/ovn-controller.h
@@ -46,6 +46,17 @@ struct ct_zone_pending_entry {
     enum ct_zone_pending_state state;
 };
 
+/* Represents a peer datapath connected to a given datapath */
+struct peer_datapath {
+    const struct sbrec_datapath_binding *peer_dp;
+
+    /* Patch port connected to local datapath */
+    const struct sbrec_port_binding *patch;
+
+    /* Peer patch port connected to peer datapath */
+    const struct sbrec_port_binding *peer;
+};
+
 /* A logical datapath that has some relevance to this hypervisor.  A logical
  * datapath D is relevant to hypervisor H if:
  *
@@ -63,10 +74,13 @@ struct local_datapath {
     /* The localnet port in this datapath, if any (at most one is allowed). */
     const struct sbrec_port_binding *localnet_port;
 
+    /* The chassisredirect port in this datapath */
+    const struct sbrec_port_binding *chassisredirect_port;
+
     /* True if this datapath contains an l3gateway port located on this
      * hypervisor. */
     bool has_local_l3gateway;
-    const struct sbrec_datapath_binding **peer_dps;
+    struct peer_datapath **peer_dps;
     size_t n_peer_dps;
 };
 
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
index fc8adcf..820ec79 100644
--- a/ovn/controller/physical.c
+++ b/ovn/controller/physical.c
@@ -539,6 +539,47 @@ consider_port_binding(struct controller_ctx *ctx,
          * input port, MFF_LOG_DATAPATH to the logical datapath, and
          * resubmit into the logical ingress pipeline starting at table
          * 16. */
+
+        /* Match a VLAN tag and strip it. If the vlan network is connected
+         * to a router which has a gateway port on redirect-chassis,
+         * set MLF_RCV_FROM_VLAN flag, router metadata and input port to
+         * connecting patch port */
+        int vlan_tag = binding->tag ? *binding->tag : 0;
+        if (!strcmp(binding->type, "localnet") && vlan_tag) {
+            struct local_datapath *ldp = get_local_datapath(
+                local_datapaths, binding->datapath->tunnel_key);
+            for (int i = 0; i < ldp->n_peer_dps; i++) {
+                struct local_datapath *peer_ldp = get_local_datapath(
+                    local_datapaths, ldp->peer_dps[i]->peer_dp->tunnel_key);
+                const struct sbrec_port_binding *crp;
+                crp = peer_ldp->chassisredirect_port;
+                if (crp && crp->chassis &&
+                   !strcmp(crp->chassis->name, chassis->name)) {
+                    const char *gwp = smap_get(&crp->options,
+                                               "distributed-port");
+                    if (strcmp(gwp, ldp->peer_dps[i]->peer->logical_port)) {
+                        ofpbuf_clear(ofpacts_p);
+                        match_init_catchall(&match);
+
+                        match_set_in_port(&match, ofport);
+                        match_set_dl_vlan(&match, htons(vlan_tag));
+
+                        ofpact_put_STRIP_VLAN(ofpacts_p);
+                        put_load(peer_ldp->datapath->tunnel_key,
+                                 MFF_LOG_DATAPATH, 0, 64, ofpacts_p);
+                        put_load(ldp->peer_dps[i]->peer->tunnel_key,
+                                 MFF_LOG_INPORT, 0, 32, ofpacts_p);
+                        put_load(1, MFF_LOG_FLAGS,
+                                 MLF_RCV_FROM_VLAN_BIT, 1, ofpacts_p);
+                        put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
+
+                        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
+                                        150, 0, &match, ofpacts_p);
+                    }
+                }
+            }
+        }
+
         ofpbuf_clear(ofpacts_p);
         match_init_catchall(&match);
         match_set_in_port(&match, ofport);
@@ -639,6 +680,48 @@ consider_port_binding(struct controller_ctx *ctx,
          * flow matches an output port that includes a logical port on a remote
          * hypervisor, and tunnels the packet to that hypervisor.
          */
+
+        /* For each vlan network connected to the router, add that network's
+         * vlan tag to the packet and output it through localnet port */
+        struct local_datapath *ldp = get_local_datapath(local_datapaths,
+                                                        dp_key);
+        struct ofpact_vlan_vid *vlan_vid;
+        for (int i = 0; i < ldp->n_peer_dps; i++) {
+            ofp_port_t port_ofport = 0;
+            const struct peer_datapath *pdp = ldp->peer_dps[i];
+            const struct local_datapath *peer_ldp = get_local_datapath(
+                local_datapaths, pdp->peer_dp->tunnel_key);
+            const struct sbrec_port_binding *lnp = peer_ldp->localnet_port;
+            if (lnp && pdp->patch->tunnel_key) {
+                int vlan_tag = lnp->tag ? *lnp->tag : 0;
+                if (!vlan_tag) {
+                    continue;
+                }
+                port_ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
+                                         lnp->logical_port));
+                if (!port_ofport) {
+                    continue;
+                }
+
+                match_init_catchall(&match);
+                ofpbuf_clear(ofpacts_p);
+
+                match_set_metadata(&match, htonll(dp_key));
+                match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0,
+                              pdp->patch->tunnel_key);
+                match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
+
+                vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts_p);
+                vlan_vid->vlan_vid = vlan_tag;
+                vlan_vid->push_vlan_if_needed = true;
+                ofpact_put_OUTPUT(ofpacts_p)->port = port_ofport;
+                ofpact_put_STRIP_VLAN(ofpacts_p);
+
+                ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
+                                &match, ofpacts_p);
+            }
+        }
+
         match_init_catchall(&match);
         ofpbuf_clear(ofpacts_p);
 
diff --git a/ovn/lib/logical-fields.c b/ovn/lib/logical-fields.c
index a8b5e3c..b9efa02 100644
--- a/ovn/lib/logical-fields.c
+++ b/ovn/lib/logical-fields.c
@@ -105,6 +105,10 @@ ovn_init_symtab(struct shash *symtab)
              MLF_FORCE_SNAT_FOR_LB_BIT);
     expr_symtab_add_subfield(symtab, "flags.force_snat_for_lb", NULL,
                              flags_str);
+    snprintf(flags_str, sizeof flags_str, "flags[%d]",
+             MLF_RCV_FROM_VLAN_BIT);
+    expr_symtab_add_subfield(symtab, "flags.rcv_from_vlan", NULL,
+                             flags_str);
 
     /* Connection tracking state. */
     expr_symtab_add_field(symtab, "ct_mark", MFF_CT_MARK, NULL, false);
diff --git a/ovn/lib/logical-fields.h b/ovn/lib/logical-fields.h
index b1dbb03..96250fd 100644
--- a/ovn/lib/logical-fields.h
+++ b/ovn/lib/logical-fields.h
@@ -50,6 +50,7 @@ enum mff_log_flags_bits {
     MLF_FORCE_SNAT_FOR_DNAT_BIT = 2,
     MLF_FORCE_SNAT_FOR_LB_BIT = 3,
     MLF_LOCAL_ONLY_BIT = 4,
+    MLF_RCV_FROM_VLAN_BIT = 5,
 };
 
 /* MFF_LOG_FLAGS_REG flag assignments */
@@ -75,6 +76,7 @@ enum mff_log_flags {
      * hypervisors should instead only be output to local targets
      */
     MLF_LOCAL_ONLY = (1 << MLF_LOCAL_ONLY_BIT),
+    MLF_RCV_FROM_VLAN = (1 << MLF_RCV_FROM_VLAN_BIT),
 };
 
 #endif /* ovn/lib/logical-fields.h */
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 0e06776..f08b3f4 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -4822,6 +4822,19 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap 
*ports,
         }
         ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
                       ds_cstr(&match), "next;");
+
+        /* VLAN traffic from localnet port should be allowed for
+         * router processing on the "redirect-chassis". */
+        if (op->od->l3dgw_port && op->od->l3redirect_port && op->peer &&
+            op->peer->od->localnet_port && (op != op->od->l3dgw_port)) {
+            ds_clear(&match);
+            ds_put_format(&match, "flags.rcv_from_vlan == 1");
+            ds_put_format(&match, " && inport == %s", op->json_key);
+            ds_put_format(&match, " && is_chassis_resident(%s)",
+                          op->od->l3redirect_port->json_key);
+            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 100,
+                          ds_cstr(&match), "next;");
+        }
     }
 
     /* Logical router ingress table 1: IP Input. */
@@ -5870,6 +5883,25 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap 
*ports,
         }
     }
 
+    /* Traffic from VLAN network to external network should be redirected
+     * "redirect-chassis" by setting REGBIT_NAT_REDIRECT flag.
+     * Later physical table 32 will output this traffic to gateway
+     * chassis using vlan networks */
+    HMAP_FOR_EACH (op, key_node, ports) {
+        if (op->nbrp && op->od->l3redirect_port && op->peer &&
+            op->peer->od->localnet_port &&
+            (op != op->od->l3dgw_port)) {
+            ds_clear(&match);
+            ds_put_format(&match, "inport == %s", op->json_key);
+            ds_put_format(&match, " && !is_chassis_resident(%s)",
+                          op->od->l3redirect_port->json_key);
+
+            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 1,
+                          ds_cstr(&match),
+                          REGBIT_NAT_REDIRECT" = 1; next;");
+        }
+    }
+
     /* Convert the static routes to flows. */
     HMAP_FOR_EACH (od, key_node, datapaths) {
         if (!od->nbr) {
-- 
1.8.3.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to