[ovs-dev] [PATCH v8 3/3 ovn] OVN: Vlan backed DVR N-S, redirect packet via localnet port
Numan Siddique
nusiddiq at redhat.com
Wed Aug 28 16:12:32 UTC 2019
On Wed, Aug 28, 2019 at 7:27 AM Ankur Sharma <ankur.sharma at nutanix.com>
wrote:
> Background:
> With c0974331b7a19a87ab8f1f2cec8fbe366af92fa2, we have added
> support for E-W workflow for vlan backed DVRs.
>
> This series enables N-S workflow for vlan backed DVRs.
>
> Key difference between E-W and N-S traffic flow is that
> N-S flow requires a gateway chassis. A gateway chassis
> will be respondible for following:
> a. Doing Network Address Translation (NAT).
> b. Becoming entry and exit point for North->South
> and South->North traffic respectively.
>
> OVN by default always uses overlay encapsulation to redirect
> the packet to gateway chassis. This series will enable
> the redirection to gateway chassis in the absence of encapsulation.
>
> This patch:
> Achieves the vlan backed redirection by doing following:
>
> Sender Side:
> ------------
> a. For a remote port of type "chassisredirect" and if it
> has redirect type as "vlan", then do not add tunnel
> based redirection flow in table=32.
>
> b. In table=33, add a flow with priority=100, that would do following:
> i. Change the metadata to that of gateway logical switch
> (i.e logical switch attached to gateway logical router port).
> ii. Change REG15 to point to localnet port of gateway logical switch.
> iii. send to packet to table=15.
>
> c. In Table=65, packet will hit the existing priority=150 flow to send
> the packet to physical bridge, while attaching vlan header and
> changing source mac to chassis mac.
>
> Receiver Side:
> --------------
> a. No changes needed
>
> OVERALL PACKET FLOW:
> Sender Side:
> -----------
> a. logical flow in lr_in_gw_redirect stage will ensure that
> outport of the packet is chassisredirect port.
> For example:
> table=12(lr_in_gw_redirect ), priority=50 , match=(outport ==
> "router-to-underlay"), action=(outport = "cr-router-to-underlay"; next;)
>
> b. After ingress pipeline, packet will enter the table=32, followed by
> table=33
>
> c. Table=33, will send the packet to table=65.
>
> d. Table=65, will send the packet to uplink bridge
> with destination mac of chassisredirect port and vlan
> id of peer logical switch.
>
> Receiver Side:
> -------------
> a. Packet is received by the pipeline of peer logical switch.
> b. Since destination mac is that of router port, hence packet will
> enter the logical router pipeline.
> c. Now, packet will go through regular logical router pipeline
> (both ingress and egress).
>
> One caveat with the approach is that ttl will be decremented twice,
> since the packets are going through logical router ingress pipeline
> twice (once on sender chassis and again on gateway chassis).
>
> No changes needed for the reverse path.
>
> Signed-off-by: Ankur Sharma <ankur.sharma at nutanix.com>
> ---
>
Hi Ankur,
I applied this patch to master with some changes in the commit message -
corrected the commit id and
typo - table 15 to table 65 and below changes
******************************************************
diff --git a/controller/physical.c b/controller/physical.c
index 427be18f8..c818646f0 100644
--- a/controller/physical.c
+++ b/controller/physical.c
@@ -228,29 +228,28 @@ get_zone_ids(const struct sbrec_port_binding *binding,
}
static void
-put_remote_port_redirect_vlan(const struct
- sbrec_port_binding *binding,
- const struct hmap *local_datapaths,
- struct local_datapath *ld,
- struct match *match,
- struct ofpbuf *ofpacts_p,
- struct ovn_desired_flow_table *flow_table)
+put_remote_port_redirect_bridged(const struct
+ sbrec_port_binding *binding,
+ const struct hmap *local_datapaths,
+ struct local_datapath *ld,
+ struct match *match,
+ struct ofpbuf *ofpacts_p,
+ struct ovn_desired_flow_table *flow_table)
{
- struct eth_addr binding_mac;
- uint32_t ls_dp_key = 0;
-
if (strcmp(binding->type, "chassisredirect")) {
- /* VLAN based redirect is only supported for chassisredirect
+ /* bridged based redirect is only supported for chassisredirect
* type remote ports. */
return;
}
+ struct eth_addr binding_mac;
bool is_valid_mac = extract_sbrec_binding_first_mac(binding,
&binding_mac);
if (!is_valid_mac) {
return;
}
+ uint32_t ls_dp_key = 0;
for (int i = 0; i < ld->n_peer_ports; i++) {
const struct sbrec_port_binding *sport_binding =
ld->peer_ports[i];
const char *sport_peer_name = smap_get(&sport_binding->options,
@@ -1011,8 +1010,9 @@ consider_port_binding(struct ovsdb_idl_index
*sbrec_port_binding_by_name,
match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
if (redirect_type && !strcasecmp(redirect_type, "bridged")) {
- put_remote_port_redirect_vlan(binding, local_datapaths,
- ld, &match, ofpacts_p,
flow_table);
+ put_remote_port_redirect_bridged(binding, local_datapaths,
+ ld, &match, ofpacts_p,
+ flow_table);
} else {
put_remote_port_redirect_overlay(binding, is_ha_remote,
ha_ch_ordered, mff_ovn_geneve,
diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml
index 366c201ad..415da59b3 100644
--- a/ovn-architecture.7.xml
+++ b/ovn-architecture.7.xml
@@ -1614,15 +1614,20 @@
</li>
</ol>
- VLAN based redirection
+ <p>
+ VLAN based redirection
- As an enhancement to <code>reside-on-redirect-chassis</code> we support
- VLAN based redirection as well. By setting
<code>options:redirect-type</code>
- to <code>vlan</code> to a gateway chassis attached router port, user can
- enforce that redirected packet should not use tunnel port but rather use
- localnet port of peer logical switch to go out as vlan packet.
+ As an enhancement to <code>reside-on-redirect-chassis</code> we support
+ VLAN based redirection as well. By setting
+ <code>options:redirect-type</code> to <code>vlan</code> to a gateway
+ chassis attached router port, user can enforce that redirected packet
+ should not use tunnel port but rather use localnet port of peer logical
+ switch to go out as vlan packet.
+ </p>
- Following happens for a VLAN based redirection:
+ <p>
+ Following happens for a VLAN based redirection:
+ </p>
<ol>
<li>
On compute chassis, packet passes though logical router's
@@ -1652,7 +1657,10 @@
</li>
</ol>
- Some guidelines and expections with VLAN based redirection:
+ <p>
+ Some guidelines and expections with VLAN based redirection:
+ </p>
+
<ol>
<li>
Since router port mac is destination mac, hence it has to be ensured
*************************************************
Thanks
Numan
controller/physical.c | 255 +++++++++++++++++++++++++++--------------
> lib/ovn-util.c | 33 ++++++
> lib/ovn-util.h | 5 +
> ovn-architecture.7.xml | 64 +++++++++++
> tests/ovn.at | 304
> +++++++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 578 insertions(+), 83 deletions(-)
>
> diff --git a/controller/physical.c b/controller/physical.c
> index 5068785..427be18 100644
> --- a/controller/physical.c
> +++ b/controller/physical.c
> @@ -228,6 +228,165 @@ get_zone_ids(const struct sbrec_port_binding
> *binding,
> }
>
> static void
> +put_remote_port_redirect_vlan(const struct
> + sbrec_port_binding *binding,
> + const struct hmap *local_datapaths,
> + struct local_datapath *ld,
> + struct match *match,
> + struct ofpbuf *ofpacts_p,
> + struct ovn_desired_flow_table *flow_table)
> +{
> + struct eth_addr binding_mac;
> + uint32_t ls_dp_key = 0;
> +
> + if (strcmp(binding->type, "chassisredirect")) {
> + /* VLAN based redirect is only supported for chassisredirect
> + * type remote ports. */
> + return;
> + }
> +
> + bool is_valid_mac = extract_sbrec_binding_first_mac(binding,
> +
> &binding_mac);
> + if (!is_valid_mac) {
> + return;
> + }
> +
> + for (int i = 0; i < ld->n_peer_ports; i++) {
> + const struct sbrec_port_binding *sport_binding =
> ld->peer_ports[i];
> + const char *sport_peer_name =
> smap_get(&sport_binding->options,
> + "peer");
> + const char *distributed_port = smap_get(&binding->options,
> + "distributed-port");
> +
> + if (!strcmp(sport_peer_name, distributed_port)) {
> + ls_dp_key = sport_binding->datapath->tunnel_key;
> + break;
> + }
> + }
> +
> + if (!ls_dp_key) {
> + return;
> + }
> +
> + union mf_value value;
> + struct ofpact_mac *src_mac;
> + const struct sbrec_port_binding *ls_localnet_port;
> +
> + ls_localnet_port = get_localnet_port(local_datapaths, ls_dp_key);
> +
> + src_mac = ofpact_put_SET_ETH_SRC(ofpacts_p);
> + src_mac->mac = binding_mac;
> +
> + value.be64 = htonll(ls_dp_key);
> +
> + ofpact_put_set_field(ofpacts_p, mf_from_id(MFF_METADATA),
> + &value, NULL);
> +
> + value.be32 = htonl(ls_localnet_port->tunnel_key);
> + ofpact_put_set_field(ofpacts_p, mf_from_id(MFF_REG15),
> + &value, NULL);
> +
> + put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
> + ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
> + match, ofpacts_p, &binding->header_.uuid);
> +
> +}
> +
> +static void
> +put_remote_port_redirect_overlay(const struct
> + sbrec_port_binding *binding,
> + bool is_ha_remote,
> + struct ha_chassis_ordered *ha_ch_ordered,
> + enum mf_field_id mff_ovn_geneve,
> + const struct chassis_tunnel *tun,
> + uint32_t port_key,
> + struct match *match,
> + struct ofpbuf *ofpacts_p,
> + struct ovn_desired_flow_table
> *flow_table)
> +{
> + if (!is_ha_remote) {
> + /* Setup encapsulation */
> + const struct chassis_tunnel *rem_tun =
> + get_port_binding_tun(binding);
> + if (!rem_tun) {
> + return;
> + }
> + put_encapsulation(mff_ovn_geneve, tun, binding->datapath,
> + port_key, ofpacts_p);
> + /* Output to tunnel. */
> + ofpact_put_OUTPUT(ofpacts_p)->port = rem_tun->ofport;
> + } else {
> + /* Make sure all tunnel endpoints use the same encapsulation,
> + * and set it up */
> + for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
> + const struct sbrec_chassis *ch =
> ha_ch_ordered->ha_ch[i].chassis;
> + if (!ch) {
> + continue;
> + }
> + if (!tun) {
> + tun = chassis_tunnel_find(ch->name, NULL);
> + } else {
> + struct chassis_tunnel *chassis_tunnel =
> + chassis_tunnel_find(ch->name,
> NULL);
> + if (chassis_tunnel &&
> + tun->type != chassis_tunnel->type) {
> + static struct vlog_rate_limit rl =
> + VLOG_RATE_LIMIT_INIT(1, 1);
> + VLOG_ERR_RL(&rl, "Port %s has Gateway_Chassis "
> + "with mixed encapsulations, only "
> + "uniform encapsulations are "
> + "supported.", binding->logical_port);
> + return;
> + }
> + }
> + }
> + if (!tun) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> + VLOG_ERR_RL(&rl, "No tunnel endpoint found for HA chassis in "
> + "HA chassis group of port %s",
> + binding->logical_port);
> + return;
> + }
> +
> + put_encapsulation(mff_ovn_geneve, tun, binding->datapath,
> + port_key, ofpacts_p);
> +
> + /* Output to tunnels with active/backup */
> + struct ofpact_bundle *bundle = ofpact_put_BUNDLE(ofpacts_p);
> +
> + for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
> + const struct sbrec_chassis *ch =
> + ha_ch_ordered->ha_ch[i].chassis;
> + if (!ch) {
> + continue;
> + }
> + tun = chassis_tunnel_find(ch->name, NULL);
> + if (!tun) {
> + continue;
> + }
> + if (bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
> + static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(1, 1);
> + VLOG_WARN_RL(&rl, "Remote endpoints for port beyond "
> + "BUNDLE_MAX_SLAVES");
> + break;
> + }
> + ofpbuf_put(ofpacts_p, &tun->ofport, sizeof tun->ofport);
> + bundle = ofpacts_p->header;
> + bundle->n_slaves++;
> + }
> +
> + bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
> + /* Although ACTIVE_BACKUP bundle algorithm seems to ignore
> + * the next two fields, those are always set */
> + bundle->basis = 0;
> + bundle->fields = NX_HASH_FIELDS_ETH_SRC;
> + ofpact_finish_BUNDLE(ofpacts_p, &bundle);
> + }
> + ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
> + match, ofpacts_p, &binding->header_.uuid);
> +}
> +
> +static void
> put_replace_router_port_mac_flows(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> const struct
> @@ -484,7 +643,8 @@ consider_port_binding(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> {
> uint32_t dp_key = binding->datapath->tunnel_key;
> uint32_t port_key = binding->tunnel_key;
> - if (!get_local_datapath(local_datapaths, dp_key)) {
> + struct local_datapath *ld;
> + if (!(ld = get_local_datapath(local_datapaths, dp_key))) {
> return;
> }
>
> @@ -830,6 +990,10 @@ consider_port_binding(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
> &match, ofpacts_p, &binding->header_.uuid);
> } else {
> +
> + const char *redirect_type = smap_get(&binding->options,
> + "redirect-type");
> +
> /* Remote port connected by tunnel */
>
> /* Table 32, priority 100.
> @@ -846,90 +1010,15 @@ consider_port_binding(struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> match_set_metadata(&match, htonll(dp_key));
> match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
>
> - if (!is_ha_remote) {
> - /* Setup encapsulation */
> - const struct chassis_tunnel *rem_tun =
> - get_port_binding_tun(binding);
> - if (!rem_tun) {
> - goto out;
> - }
> - put_encapsulation(mff_ovn_geneve, tun, binding->datapath,
> - port_key, ofpacts_p);
> - /* Output to tunnel. */
> - ofpact_put_OUTPUT(ofpacts_p)->port = rem_tun->ofport;
> + if (redirect_type && !strcasecmp(redirect_type, "bridged")) {
> + put_remote_port_redirect_vlan(binding, local_datapaths,
> + ld, &match, ofpacts_p,
> flow_table);
> } else {
> - /* Make sure all tunnel endpoints use the same encapsulation,
> - * and set it up */
> - for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
> - const struct sbrec_chassis *ch =
> - ha_ch_ordered->ha_ch[i].chassis;
> - if (!ch) {
> - continue;
> - }
> - if (!tun) {
> - tun = chassis_tunnel_find(ch->name, NULL);
> - } else {
> - struct chassis_tunnel *chassis_tunnel =
> - chassis_tunnel_find(ch->name, NULL);
> - if (chassis_tunnel &&
> - tun->type != chassis_tunnel->type) {
> - static struct vlog_rate_limit rl =
> - VLOG_RATE_LIMIT_INIT(1, 1);
> - VLOG_ERR_RL(&rl, "Port %s has Gateway_Chassis "
> - "with mixed encapsulations,
> only "
> - "uniform encapsulations are "
> - "supported.",
> - binding->logical_port);
> - goto out;
> - }
> - }
> - }
> - if (!tun) {
> - static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(1, 1);
> - VLOG_ERR_RL(&rl, "No tunnel endpoint found for HA chassis
> in "
> - "HA chassis group of port %s",
> - binding->logical_port);
> - goto out;
> - }
> -
> - put_encapsulation(mff_ovn_geneve, tun, binding->datapath,
> - port_key, ofpacts_p);
> -
> - /* Output to tunnels with active/backup */
> - struct ofpact_bundle *bundle = ofpact_put_BUNDLE(ofpacts_p);
> -
> - for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
> - const struct sbrec_chassis *ch =
> - ha_ch_ordered->ha_ch[i].chassis;
> - if (!ch) {
> - continue;
> - }
> - tun = chassis_tunnel_find(ch->name, NULL);
> - if (!tun) {
> - continue;
> - }
> - if (bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
> - static struct vlog_rate_limit rl =
> - VLOG_RATE_LIMIT_INIT(1, 1);
> - VLOG_WARN_RL(&rl, "Remote endpoints for port beyond "
> - "BUNDLE_MAX_SLAVES");
> - break;
> - }
> - ofpbuf_put(ofpacts_p, &tun->ofport,
> - sizeof tun->ofport);
> - bundle = ofpacts_p->header;
> - bundle->n_slaves++;
> - }
> -
> - bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
> - /* Although ACTIVE_BACKUP bundle algorithm seems to ignore
> - * the next two fields, those are always set */
> - bundle->basis = 0;
> - bundle->fields = NX_HASH_FIELDS_ETH_SRC;
> - ofpact_finish_BUNDLE(ofpacts_p, &bundle);
> + put_remote_port_redirect_overlay(binding, is_ha_remote,
> + ha_ch_ordered,
> mff_ovn_geneve,
> + tun, port_key, &match,
> ofpacts_p,
> + flow_table);
> }
> - ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
> - &match, ofpacts_p, &binding->header_.uuid);
> }
> out:
> if (ha_ch_ordered) {
> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> index 085498f..ebba7ad 100644
> --- a/lib/ovn-util.c
> +++ b/lib/ovn-util.c
> @@ -16,6 +16,7 @@
> #include "ovn-util.h"
> #include "dirs.h"
> #include "openvswitch/vlog.h"
> +#include "openvswitch/ofp-parse.h"
> #include "ovn-nb-idl.h"
> #include "ovn-sb-idl.h"
>
> @@ -272,6 +273,38 @@ extract_lrp_networks(const struct
> nbrec_logical_router_port *lrp,
> return true;
> }
>
> +bool
> +extract_sbrec_binding_first_mac(const struct sbrec_port_binding *binding,
> + struct eth_addr *ea)
> +{
> + char *save_ptr = NULL;
> + bool ret = false;
> +
> + if (!binding->n_mac) {
> + return ret;
> + }
> +
> + char *tokstr = xstrdup(binding->mac[0]);
> +
> + for (char *token = strtok_r(tokstr, " ", &save_ptr);
> + token != NULL;
> + token = strtok_r(NULL, " ", &save_ptr)) {
> +
> + /* Return the first chassis mac. */
> + char *err_str = str_to_mac(token, ea);
> + if (err_str) {
> + free(err_str);
> + continue;
> + }
> +
> + ret = true;
> + break;
> + }
> +
> + free(tokstr);
> + return ret;
> +}
> +
> void
> destroy_lport_addresses(struct lport_addresses *laddrs)
> {
> diff --git a/lib/ovn-util.h b/lib/ovn-util.h
> index 6d5e1df..8461db5 100644
> --- a/lib/ovn-util.h
> +++ b/lib/ovn-util.h
> @@ -21,6 +21,8 @@
> struct nbrec_logical_router_port;
> struct sbrec_logical_flow;
> struct uuid;
> +struct eth_addr;
> +struct sbrec_port_binding;
>
> struct ipv4_netaddr {
> ovs_be32 addr; /* 192.168.10.123 */
> @@ -61,6 +63,9 @@ bool extract_lsp_addresses(const char *address, struct
> lport_addresses *);
> bool extract_ip_addresses(const char *address, struct lport_addresses *);
> bool extract_lrp_networks(const struct nbrec_logical_router_port *,
> struct lport_addresses *);
> +bool extract_sbrec_binding_first_mac(const struct sbrec_port_binding
> *binding,
> + struct eth_addr *ea);
> +
> void destroy_lport_addresses(struct lport_addresses *);
>
> char *alloc_nat_zone_key(const struct uuid *key, const char *type);
> diff --git a/ovn-architecture.7.xml b/ovn-architecture.7.xml
> index c4099f2..366c201 100644
> --- a/ovn-architecture.7.xml
> +++ b/ovn-architecture.7.xml
> @@ -1614,6 +1614,70 @@
> </li>
> </ol>
>
> + VLAN based redirection
> +
> + As an enhancement to <code>reside-on-redirect-chassis</code> we support
> + VLAN based redirection as well. By setting
> <code>options:redirect-type</code>
> + to <code>vlan</code> to a gateway chassis attached router port, user can
> + enforce that redirected packet should not use tunnel port but rather use
> + localnet port of peer logical switch to go out as vlan packet.
> +
> + Following happens for a VLAN based redirection:
> + <ol>
> + <li>
> + On compute chassis, packet passes though logical router's
> + ingress pipeline.
> + </li>
> +
> + <li>
> + If logical outport is gateway chassis attached router port
> + then packet is "redirected" to gateway chassis using peer logical
> + switch's localnet port.
> + </li>
> +
> + <li>
> + This VLAN backed redirected packet has destination mac
> + as router port mac (the one to which gateway chassis is attached)
> and
> + vlan id is that of localnet port (peer logical switch of
> + the logical router port).
> + </li>
> +
> + <li>
> + On the gateway chassis packet will enter the logical router pipeline
> + again and this time it will passthrough egress pipeline as well.
> + </li>
> +
> + <li>
> + Reverse traffic packet flows stays the same.
> + </li>
> + </ol>
> +
> + Some guidelines and expections with VLAN based redirection:
> + <ol>
> + <li>
> + Since router port mac is destination mac, hence it has to be ensured
> + that physical network learns it on ONLY from the gateway chassis.
> + Which means that <code>ovn-chassis-mac-mappings</code> should be
> + configure on all the compute nodes, so that physical network
> + never learn router port mac from compute nodes.
> + </li>
> +
> + <li>
> + Since packet enters logical router ingress pipeline twice
> + (once on compute chassis and again on gateway chassis),
> + hence ttl will be decremented twice.
> + </li>
> +
> + <li>
> + Default redirection type continues to be <code>overlay</code>.
> + User can switch the redirect-type between <code>vlan</code>
> + and <code>overlay</code> by changing the value of
> + <code>options:redirect-type</code>
> + </li>
> +
> + </ol>
> +
> +
> <h2>Life Cycle of a VTEP gateway</h2>
>
> <p>
> diff --git a/tests/ovn.at b/tests/ovn.at
> index c5281a0..7bc25b1 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -15492,3 +15492,307 @@ as hv4 ovs-appctl fdb/show br-phys
> OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
>
> AT_CLEANUP
> +
> +AT_SETUP([ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR N-S Ping])
> +ovn_start
> +
> +# In this test cases we create 3 switches, all connected to same
> +# physical network (through br-phys on each HV). LS1 and LS2 have
> +# 1 VIF each. Each HV has 1 VIF port. The first digit
> +# of VIF port name indicates the hypervisor it is bound to, e.g.
> +# lp23 means VIF 3 on hv2.
> +#
> +# All the switches are connected to a logical router "router".
> +#
> +# Each switch's VLAN tag and their logical switch ports are:
> +# - ls1:
> +# - tagged with VLAN 101
> +# - ports: lp11
> +# - ls2:
> +# - tagged with VLAN 201
> +# - ports: lp22
> +# - ls-underlay:
> +# - tagged with VLAN 1000
> +# Note: a localnet port is created for each switch to connect to
> +# physical network.
> +
> +for i in 1 2; do
> + ls_name=ls$i
> + ovn-nbctl ls-add $ls_name
> + ln_port_name=ln$i
> + if test $i -eq 1; then
> + ovn-nbctl lsp-add $ls_name $ln_port_name "" 101
> + elif test $i -eq 2; then
> + ovn-nbctl lsp-add $ls_name $ln_port_name "" 201
> + fi
> + 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
> +done
> +
> +# lsp_to_ls LSP
> +#
> +# Prints the name of the logical switch that contains LSP.
> +lsp_to_ls () {
> + case $1 in dnl (
> + lp?[[11]]) echo ls1 ;; dnl (
> + lp?[[12]]) echo ls2 ;; dnl (
> + *) AT_FAIL_IF([:]) ;;
> + esac
> +}
> +
> +vif_to_hv () {
> + case $1 in dnl (
> + vif[[1]]?) echo hv1 ;; dnl (
> + vif[[2]]?) echo hv2 ;; dnl (
> + vif?[[north]]?) echo hv4 ;; dnl (
> + *) AT_FAIL_IF([:]) ;;
> + esac
> +}
> +
> +ip_to_hex() {
> + printf "%02x%02x%02x%02x" "$@"
> +}
> +
> +net_add n1
> +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:br-phys
> + ovs-vsctl set open .
> external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i"
> + ovn_attach n1 br-phys 192.168.0.$i
> +
> + ovs-vsctl add-port br-int vif$i$i -- \
> + set Interface vif$i$i external-ids:iface-id=lp$i$i \
> + options:tx_pcap=hv$i/vif$i$i-tx.pcap \
> + options:rxq_pcap=hv$i/vif$i$i-rx.pcap \
> + ofport-request=$i$i
> +
> + lsp_name=lp$i$i
> + ls_name=$(lsp_to_ls $lsp_name)
> +
> + ovn-nbctl lsp-add $ls_name $lsp_name
> + ovn-nbctl lsp-set-addresses $lsp_name "f0:00:00:00:00:$i$i
> 192.168.$i.$i"
> + ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$i
> +
> + OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
> +
> +done
> +
> +ovn-nbctl ls-add ls-underlay
> +ovn-nbctl lsp-add ls-underlay ln3 "" 1000
> +ovn-nbctl lsp-set-addresses ln3 unknown
> +ovn-nbctl lsp-set-type ln3 localnet
> +ovn-nbctl lsp-set-options ln3 network_name=phys
> +
> +ovn-nbctl ls-add ls-north
> +ovn-nbctl lsp-add ls-north ln4 "" 1000
> +ovn-nbctl lsp-set-addresses ln4 unknown
> +ovn-nbctl lsp-set-type ln4 localnet
> +ovn-nbctl lsp-set-options ln4 network_name=phys
> +
> +# Add a VM on ls-north
> +ovn-nbctl lsp-add ls-north lp-north
> +ovn-nbctl lsp-set-addresses lp-north "f0:f0:00:00:00:11 172.31.0.10"
> +ovn-nbctl lsp-set-port-security lp-north f0:f0:00:00:00:11
> +
> +# Add 3rd hypervisor
> +sim_add hv3
> +as hv3 ovs-vsctl add-br br-phys
> +as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
> +as hv3 ovs-vsctl set open .
> external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:33"
> +as hv3 ovn_attach n1 br-phys 192.168.0.3
> +
> +# Add 4th hypervisor
> +sim_add hv4
> +as hv4 ovs-vsctl add-br br-phys
> +as hv4 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
> +as hv4 ovs-vsctl set open .
> external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:44"
> +as hv4 ovn_attach n1 br-phys 192.168.0.4
> +
> +as hv4 ovs-vsctl add-port br-int vif-north -- \
> + set Interface vif-north external-ids:iface-id=lp-north \
> + options:tx_pcap=hv4/vif-north-tx.pcap \
> + options:rxq_pcap=hv4/vif-north-rx.pcap \
> + ofport-request=44
> +
> +ovn-nbctl lr-add router
> +ovn-nbctl lrp-add router router-to-ls1 00:00:01:01:02:03 192.168.1.3/24
> +ovn-nbctl <http://192.168.1.3/24+ovn-nbctl> lrp-add router router-to-ls2
> 00:00:01:01:02:05 192.168.2.3/24
> +ovn-nbctl <http://192.168.2.3/24+ovn-nbctl> lrp-add router
> router-to-underlay 00:00:01:01:02:07 172.31.0.1/24
> +
> +ovn-nbctl lsp-add ls1 ls1-to-router -- set Logical_Switch_Port
> ls1-to-router type=router \
> + options:router-port=router-to-ls1 -- lsp-set-addresses
> ls1-to-router router
> +ovn-nbctl lsp-add ls2 ls2-to-router -- set Logical_Switch_Port
> ls2-to-router type=router \
> + options:router-port=router-to-ls2 -- lsp-set-addresses
> ls2-to-router router
> +ovn-nbctl lsp-add ls-underlay underlay-to-router -- set
> Logical_Switch_Port \
> + underlay-to-router type=router \
> + options:router-port=router-to-underlay \
> + -- lsp-set-addresses underlay-to-router
> router
> +
> +ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv3
> +ovn-nbctl lrp-set-redirect-type router-to-underlay bridged
> +
> +ovn-nbctl --wait=sb sync
> +
> +
> +OVN_POPULATE_ARP
> +
> +# lsp_to_ls LSP
> +#
> +# Prints the name of the logical switch that contains LSP.
> +lsp_to_ls () {
> + case $1 in dnl (
> + lp?[[11]]) echo ls1 ;; dnl (
> + lp?[[12]]) echo ls2 ;; dnl (
> + *) AT_FAIL_IF([:]) ;;
> + esac
> +}
> +
> +vif_to_ls () {
> + case $1 in dnl (
> + vif?[[11]]) echo ls1 ;; dnl (
> + vif?[[12]]) echo ls2 ;; dnl (
> + vif-north) echo ls-north ;; dnl (
> + *) AT_FAIL_IF([:]) ;;
> + esac
> +}
> +
> +hv_to_num () {
> + case $1 in dnl (
> + hv1) echo 1 ;; dnl (
> + hv2) echo 2 ;; dnl (
> + hv3) echo 3 ;; dnl (
> + hv4) echo 4 ;; dnl (
> + *) AT_FAIL_IF([:]) ;;
> + esac
> +}
> +
> +vif_to_num () {
> + case $1 in dnl (
> + vif22) echo 22 ;; dnl (
> + vif21) echo 21 ;; dnl (
> + vif11) echo 11 ;; dnl (
> + *) AT_FAIL_IF([:]) ;;
> + esac
> +}
> +
> +vif_to_hv () {
> + case $1 in dnl (
> + vif[[1]]?) echo hv1 ;; dnl (
> + vif[[2]]?) echo hv2 ;; dnl (
> + vif-north) echo hv4 ;; dnl (
> + *) AT_FAIL_IF([:]) ;;
> + esac
> +}
> +
> +vif_to_lrp () {
> + echo router-to-`vif_to_ls $1`
> +}
> +
> +ip_to_hex() {
> + printf "%02x%02x%02x%02x" "$@"
> +}
> +
> +
> +test_ip() {
> + # This packet has bad checksums but logical L3 routing doesn't
> check.
> + local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
> outport=$6
> + local
> packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
> + shift; shift; shift; shift; shift
> + hv=`vif_to_hv $inport`
> + as $hv ovs-appctl netdev-dummy/receive $inport $packet
> + in_ls=`vif_to_ls $inport`
> + for outport; do
> + out_ls=`vif_to_ls $outport`
> + if test $in_ls = $out_ls; then
> + # Ports on the same logical switch receive exactly the
> same packet.
> + echo $packet
> + else
> + # Routing decrements TTL and updates source and dest MAC
> + # (and checksum).
> + out_lrp=`vif_to_lrp $outport`
> + # For North-South, packet will come via gateway chassis,
> i.e hv3
> + if test $inport = vif-north; then
> + echo
> f00000000011aabbccddee3308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
> >> $outport.expected
> + fi
> + if test $outport = vif-north; then
> + echo
> f0f00000001100000101020708004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
> >> $outport.expected
> + fi
> + fi >> $outport.expected
> + done
> +}
> +
> +# Dump a bunch of info helpful for debugging if there's a failure.
> +
> +echo "------ OVN dump ------"
> +ovn-nbctl show
> +ovn-sbctl show
> +ovn-sbctl list port_binding
> +ovn-sbctl list mac_binding
> +
> +echo "------ hv1 dump ------"
> +as hv1 ovs-vsctl show
> +as hv1 ovs-vsctl list Open_Vswitch
> +
> +echo "------ hv2 dump ------"
> +as hv2 ovs-vsctl show
> +as hv2 ovs-vsctl list Open_Vswitch
> +
> +echo "------ hv3 dump ------"
> +as hv3 ovs-vsctl show
> +as hv3 ovs-vsctl list Open_Vswitch
> +
> +echo "------ hv4 dump ------"
> +as hv4 ovs-vsctl show
> +as hv4 ovs-vsctl list Open_Vswitch
> +
> +echo "Send traffic North to South"
> +
> +sip=`ip_to_hex 172 31 0 10`
> +dip=`ip_to_hex 192 168 1 1`
> +test_ip vif-north f0f000000011 000001010207 $sip $dip vif11
> +
> +# Confirm that North to south traffic works fine.
> +OVN_CHECK_PACKETS([hv1/vif11-tx.pcap], [vif11.expected])
> +
> +echo "Send traffic South to Nouth"
> +sip=`ip_to_hex 192 168 1 1`
> +dip=`ip_to_hex 172 31 0 10`
> +test_ip vif11 f00000000011 000001010203 $sip $dip vif-north
> +
> +# Confirm that South to North traffic works fine.
> +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap],
> [vif-north.expected])
> +
> +# Confirm that packets did not go out via tunnel port.
> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=32 | grep
> NXM_NX_TUN_METADATA0 | grep n_packets=0 | wc -l], [0], [[0
> +]])
> +
> +# Confirm that packet went out via localnet port
> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=65 | grep
> priority=150 | grep src=00:00:01:01:02:07 | grep n_packets=1 | wc -l], [0],
> [[1
> +]])
> +
> +echo "----------- Post Traffic hv1 dump -----------"
> +as hv1 ovs-ofctl dump-flows br-int
> +as hv1 ovs-ofctl show br-phys
> +as hv1 ovs-appctl fdb/show br-phys
> +
> +echo "----------- Post Traffic hv2 dump -----------"
> +as hv2 ovs-ofctl dump-flows br-int
> +as hv2 ovs-ofctl show br-phys
> +as hv2 ovs-appctl fdb/show br-phys
> +
> +echo "----------- Post Traffic hv3 dump -----------"
> +as hv3 ovs-ofctl dump-flows br-int
> +as hv3 ovs-ofctl show br-phys
> +as hv3 ovs-appctl fdb/show br-phys
> +
> +echo "----------- Post Traffic hv4 dump -----------"
> +as hv4 ovs-ofctl dump-flows br-int
> +as hv4 ovs-ofctl show br-phys
> +as hv4 ovs-appctl fdb/show br-phys
> +
> +OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
> +
> +AT_CLEANUP
> --
> 1.8.3.1
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
More information about the dev
mailing list