From patchwork Thu May 30 00:20:23 2019
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Ankur Sharma
external_ids:ovn-chassis-mac-mappings
physnet1:aa:bb:cc:dd:ee:ff,physnet2:a1:b2:c3:d4:e5:f6
.
+ These are the macs that ovn-controller will replace a router port
+ mac with, if packet is going from a distributed router port on
+ vlan type logical switch.
+
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 6019016..315a88b 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -899,7 +899,7 @@ en_runtime_data_cleanup(struct engine_node *node)
struct local_datapath *cur_node, *next_node;
HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
&data->local_datapaths) {
- free(cur_node->peer_dps);
+ free(cur_node->peer_ports);
hmap_remove(&data->local_datapaths, &cur_node->hmap_node);
free(cur_node);
}
@@ -929,7 +929,7 @@ en_runtime_data_run(struct engine_node *node)
} else {
struct local_datapath *cur_node, *next_node;
HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, local_datapaths) {
- free(cur_node->peer_dps);
+ free(cur_node->peer_ports);
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 6afd727..a4c1309 100644
--- a/ovn/controller/ovn-controller.h
+++ b/ovn/controller/ovn-controller.h
@@ -59,8 +59,9 @@ struct local_datapath {
/* True if this datapath contains an l3gateway port located on this
* hypervisor. */
bool has_local_l3gateway;
- const struct sbrec_datapath_binding **peer_dps;
- size_t n_peer_dps;
+
+ const struct sbrec_port_binding **peer_ports;
+ size_t n_peer_ports;
};
struct local_datapath *get_local_datapath(const struct hmap *,
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
index c8dc282..af587a5 100644
--- a/ovn/controller/physical.c
+++ b/ovn/controller/physical.c
@@ -20,6 +20,7 @@
#include "ha-chassis.h"
#include "lflow.h"
#include "lport.h"
+#include "chassis.h"
#include "lib/bundle.h"
#include "openvswitch/poll-loop.h"
#include "lib/uuid.h"
@@ -30,6 +31,7 @@
#include "openvswitch/ofp-actions.h"
#include "openvswitch/ofpbuf.h"
#include "openvswitch/vlog.h"
+#include "openvswitch/ofp-parse.h"
#include "ovn-controller.h"
#include "ovn/lib/chassis-index.h"
#include "ovn/lib/ovn-sb-idl.h"
@@ -236,6 +238,92 @@ get_zone_ids(const struct sbrec_port_binding *binding,
}
static void
+put_replace_router_port_mac_flows(const struct
+ sbrec_port_binding *localnet_port,
+ const struct sbrec_chassis *chassis,
+ const struct hmap *local_datapaths,
+ struct ofpbuf *ofpacts_p,
+ ofp_port_t ofport,
+ struct ovn_desired_flow_table *flow_table)
+{
+ struct local_datapath *ld = get_local_datapath(local_datapaths,
+ localnet_port->datapath->
+ tunnel_key);
+ ovs_assert(ld);
+
+ uint32_t dp_key = localnet_port->datapath->tunnel_key;
+ uint32_t port_key = localnet_port->tunnel_key;
+ int tag = localnet_port->tag ? *localnet_port->tag : 0;
+ const char *network = smap_get(&localnet_port->options, "network_name");
+ struct eth_addr chassis_mac;
+
+ if (!network) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ VLOG_WARN_RL(&rl, "Physical network not configured for datapath: %ld "
+ "with localnet port",
+ localnet_port->datapath->tunnel_key);
+ return;
+ }
+
+ /* Get chassis mac */
+ if (!chassis_get_mac(chassis, network, &chassis_mac)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ /* Keeping the log level low for backward compatibility.
+ * Chassis mac is a new configuration.
+ */
+ VLOG_DBG_RL(&rl, "Could not get chassis mac for network: %s", network);
+ return;
+ }
+
+ for (int i = 0; i < ld->n_peer_ports; i++) {
+ const struct sbrec_port_binding *rport_binding = ld->peer_ports[i];
+ struct eth_addr router_port_mac;
+ char *err_str = NULL;
+ struct match match;
+ struct ofpact_mac *replace_mac;
+
+ /* Table 65, priority 150.
+ * =======================
+ *
+ * Implements output to localnet port.
+ * a. Flow replaces ingress router port mac with a chassis mac.
+ * b. Flow appends the vlan id localnet port is configured with.
+ */
+ match_init_catchall(&match);
+ ofpbuf_clear(ofpacts_p);
+
+ ovs_assert(rport_binding->n_mac == 1);
+ if ((err_str = str_to_mac(rport_binding->mac[0], &router_port_mac))) {
+ /* Parsing of mac failed. */
+ VLOG_WARN("Parsing or router port mac failed for router port: %s, "
+ "with error: %s", rport_binding->logical_port, err_str);
+ free(err_str);
+ return;
+ }
+
+ /* Replace Router mac flow */
+ match_set_metadata(&match, htonll(dp_key));
+ match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
+ match_set_dl_src(&match, router_port_mac);
+
+ replace_mac = ofpact_put_SET_ETH_SRC(ofpacts_p);
+ replace_mac->mac = chassis_mac;
+
+ if (tag) {
+ struct ofpact_vlan_vid *vlan_vid;
+ vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts_p);
+ vlan_vid->vlan_vid = tag;
+ vlan_vid->push_vlan_if_needed = true;
+ }
+
+ ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
+
+ ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 150, 0,
+ &match, ofpacts_p, &localnet_port->header_.uuid);
+ }
+}
+
+static void
put_local_common_flows(uint32_t dp_key, uint32_t port_key,
uint32_t parent_port_key,
const struct zone_ids *zone_ids,
@@ -707,6 +795,13 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
}
ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
&match, ofpacts_p, &binding->header_.uuid);
+
+ if (!strcmp(binding->type, "localnet")) {
+ put_replace_router_port_mac_flows(binding, chassis,
+ local_datapaths, ofpacts_p,
+ ofport, flow_table);
+ }
+
} else if (!tun && !is_ha_remote) {
/* Remote port connected by localnet port */
/* Table 33, priority 100.
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index de0c06d..74d3692 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -86,6 +86,12 @@ enum ovn_datapath_type {
DP_ROUTER /* OVN logical router. */
};
+/* Network type of a datapath */
+enum ovn_datapath_nw_type {
+ DP_NETWORK_OVERLAY,
+ DP_NETWORK_BRIDGED
+};
+
/* Returns an "enum ovn_stage" built from the arguments.
*
* (It's better to use ovn_stage_build() for type-safety reasons, but inline
@@ -445,6 +451,8 @@ struct ovn_datapath {
bool has_unknown;
+ enum ovn_datapath_nw_type network_type;
+
/* IPAM data. */
struct ipam_info ipam_info;
@@ -491,6 +499,27 @@ cleanup_macam(struct hmap *macam_)
}
}
+static void
+ovn_datapath_update_nw_type(struct ovn_datapath *od)
+{
+ if (!od->nbs) {
+ return;
+ }
+
+ if (!od->nbs->network_type ||
+ !strlen(od->nbs->network_type) ||
+ !strcmp(od->nbs->network_type, "overlay")) {
+ /* No value in network_type is taken as OVERLAY. */
+ od->network_type = DP_NETWORK_OVERLAY;
+ } else if (!strcmp(od->nbs->network_type, "bridged")) {
+ od->network_type = DP_NETWORK_BRIDGED;
+ } else {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad network type %s, for %s",
+ od->nbs->network_type, od->nbs->name);
+ }
+}
+
static struct ovn_datapath *
ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,
const struct nbrec_logical_switch *nbs,
@@ -682,6 +711,13 @@ ovn_datapath_update_external_ids(struct ovn_datapath *od)
if (name2 && name2[0]) {
smap_add(&ids, "name2", name2);
}
+
+ if (od->nbs) {
+ smap_add(&ids, "network-type",
+ (od->nbs->network_type && strlen(od->nbs->network_type)) ?
+ od->nbs->network_type : "overlay");
+ }
+
sbrec_datapath_binding_set_external_ids(od->sb, &ids);
smap_destroy(&ids);
}
@@ -734,9 +770,11 @@ join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
ovs_list_remove(&od->list);
ovs_list_push_back(both, &od->list);
ovn_datapath_update_external_ids(od);
+ ovn_datapath_update_nw_type(od);
} else {
od = ovn_datapath_create(datapaths, &nbs->header_.uuid,
nbs, NULL, NULL);
+ ovn_datapath_update_nw_type(od);
ovs_list_push_back(nb_only, &od->list);
}
diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml
index 8c9e106..6275db1 100644
--- a/ovn/ovn-architecture.7.xml
+++ b/ovn/ovn-architecture.7.xml
@@ -1407,6 +1407,30 @@
egress pipeline of the destination localnet logical switch datapath
and goes out of the integration bridge to the provider bridge (
belonging to the destination logical switch) via the localnet port.
+ While sending the packet to provider bridge, we also replace router
+ port mac as source mac with a chassis unique mac.
+
+ This chassis unique mac is configured as global ovs config on each
+ chassis (eg. via "ovs-vsctl set open . external-ids:
+ ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i"
").More
+ details on this config are present in ovn-controller
(8).
+
+ If the above is not configured, then source mac would be the router
+ port mac. This could create problem if we have more than one chassis.
+ This is because, since the router port is distributed, hence same
+ mac,vlan tuple will seen by physical network from other chassis
+ as well. This could cause some/all of these issues:
+
+ Whether logical switch will fully virtualize the network (i.e overlay) + or it simply connects to the physical network (i.e bridged). + This field will take either of the following values: "overlay" or + "bridged". +
+ ++ An "overlay" type logical switch means that 24 bit virtual network + identifier defines its broadcast domain and hence packets leaving + the chassis will be encapsulated. A "bridged" logical switch means that + it uses 12 bit vlan id as broadcast domain and packets leaving the + chassis would not be encapsulated, but would have a vlan header + instead (logical switches with vlan zero are also to be assigned the + type as "bridged"). For a "bridged" type logical switch, a localnet + port MUST be created. +
+ovn-controller
(8) for more information.
ovn-controller
populates this key with the set of options
+ configured in the column of the
+ Open_vSwitch database's
+ table. See ovn-controller
(8) for more information.
+ Common
Columns
at the beginning of this document.
@@ -2162,6 +2170,13 @@ tcp.flags = RST;
the database.
+ ovn-northd
stores in this key the network type from
+ corresponding row in
+ the database.
+ The following happens when a VM sends an external traffic (which requires @@ -1607,6 +1607,91 @@ +
+ The following happens when a VM sends an external traffic (i.e to non + logical router connected network), but there is not need for NATing. +
+ +
+ Since, there is no NATing required, hence we need not redirect the packet
+ to a gateway chassis. As a result, this packet flow is same as East-West.
+ In order to ensure that OVN will not redirect the packet over a tunnel
+ to gateway-chassis, "network_type" of destination localnet logical switch,
+ should be set as "bridged". A "bridged" logical switch ensures that there
+ is no tunnel encapsulation done while forwarding the packet on it.
+ Please refer to ovn-nb
(5) for more details.
+
+ The following happens for the reverse external traffic. +
+ ++ One thing to note here is that, while VM to External traffic did not + require redirection to gateway chassis, the reverse traffic is through + gateway chassis only. This is because, for external router, OVN logical + router port IP will be the next hop to reach the endpoints behind it. + As a result, we need a centralized chassis, which will respond to ARP + requests coming from external network. This centralized chassis, is the + gateway chassis which is attached to corresponding router port. +
+diff --git a/tests/ovn.at b/tests/ovn.at index e5108a7..8a03393 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -29,6 +29,12 @@ m4_define([OVN_CHECK_PACKETS], [ovn_check_packets__ "$1" "$2" AT_CHECK([sort $rcv_text], [0], [expout])]) +m4_define([OVN_CHECK_PACKETS_REMOVE_BROADCAST], + [ovn_check_packets__ "$1" "$2" + echo "received_text=$rcv_text" + sed -i '/ffffffffffff/d' $rcv_text + AT_CHECK([sort $rcv_text], [0], [expout])]) + AT_BANNER([OVN components]) AT_SETUP([ovn -- lexer]) @@ -14018,7 +14024,7 @@ ovn-hv4-0 OVN_CLEANUP([hv1], [hv2], [hv3]) AT_CLEANUP -AT_SETUP([ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR chassis mac]) +AT_SETUP([ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR E-W chassis mac]) ovn_start @@ -14028,6 +14034,8 @@ ovn_start # of VIF port name indicates the hypervisor it is bound to, e.g. # lp23 means VIF 3 on hv2. # +# Both 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 @@ -14185,6 +14193,7 @@ test_ip() { echo "------ OVN dump ------" ovn-nbctl show ovn-sbctl show +ovn-sbctl list port_binding echo "------ hv1 dump ------" as hv1 ovs-vsctl show @@ -14211,6 +14220,727 @@ as hv2 ovs-appctl fdb/show br-phys OVN_CHECK_PACKETS([hv2/vif22-tx.pcap], [vif22.expected]) + +# Associate a chassis as gateway chassis and validate garp. + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP + + +AT_SETUP([ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR N-S GARP]) +ovn_start + + +# In this test cases we create 2 switches, all connected to same +# physical network (through br-phys on each HV). Each switch has +# 1 VIF. 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. +# +# Both the switches are connected to a logical router "router". +# +# Additionally, we create a logical switch (ls-underlay) for N-S traffic. +# +# 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. +# lsp_to_ls LSP +# +# Prints the name of the logical switch that contains LSP. + +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" + ovs-vsctl set open . external-ids:system-id="HV$i" + ovn_attach n1 br-phys 192.168.0.$i + ovs-vsctl set-controller br-int ptcp: + AT_CHECK([ovs-vsctl add-port br-phys snoopvif -- set Interface snoopvif options:tx_pcap=hv$i/snoopvif-tx.pcap options:rxq_pcap=hv$i/snoopvif-rx.pcap]) +done + +ovn-nbctl ls-add ls-underlay bridged +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 lr-add router +ovn-nbctl lrp-add router router-to-underlay 00:00:01:01:02:07 172.31.0.1/24 + +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 --wait=sb sync + +# Associate hv2 as gateway chassis +ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv2 + +ovn-nbctl show +ovn-sbctl show + +# Dump a bunch of info helpful for debugging if there's a failure. + +echo "------ OVN dump ------" +ovn-nbctl show +ovn-sbctl show + +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 + +sleep 1 + +echo "----------- Post Traffic hv1 dump -----------" +as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int +as hv1 ovs-appctl fdb/show br-phys + +echo "----------- Post Traffic hv2 dump -----------" +as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int +as hv2 ovs-appctl fdb/show br-phys + +AT_CHECK([as hv2 ovs-appctl fdb/show br-phys | grep 00:00:01:01:02:07 | grep 1000 | wc -l], [0], [[1 +]]) + +echo "ffffffffffff000001010207810003e808060001080006040001000001010207ac1f0001000000000000ac1f0001" > expected +OVN_CHECK_PACKETS([hv2/snoopvif-tx.pcap], [expected]) + OVN_CLEANUP([hv1],[hv2]) 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 bridged + 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 bridged +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 bridged +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 lrp-add router router-to-ls2 00:00:01:01:02:05 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 --wait=sb sync + +sleep 2 + +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 f0f000000011aabbccddee1108004500001c000000003f110100${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 + +sleep 1 + +# Confirm that North to south traffic works fine and went through gateway chassis, i.e HV3 +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 + +sleep 1 + +# 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], [[1 +]]) + +# Confirm that HV1 chassis mac is never seen on Gateway chassis, i.e HV3 +AT_CHECK([as hv3 ovs-appctl fdb/show br-phys | grep aa:bb:cc:dd:ee:11 | wc -l], [0], [[0 +]]) + +echo "----------- Post Traffic hv1 dump -----------" +as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int +as hv1 ovs-appctl fdb/show br-phys + +echo "----------- Post Traffic hv2 dump -----------" +as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int +as hv2 ovs-appctl fdb/show br-phys + +echo "----------- Post Traffic hv3 dump -----------" +as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int +as hv3 ovs-appctl fdb/show br-phys + +echo "----------- Post Traffic hv4 dump -----------" +as hv4 ovs-ofctl -O OpenFlow13 dump-flows br-int +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 ARP handling]) +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 bridged + 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 bridged +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 bridged +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 lrp-add router router-to-ls2 00:00:01:01:02:05 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_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" "$@" +} + +# 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 + +# test_arp INPORT SHA SPA TPA [REPLY_HA] +# +# Causes a packet to be received on INPORT. The packet is an ARP +# request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then +# it should be the hardware address of the target to expect to receive in an +# ARP reply; otherwise no reply is expected. +# +# INPORT is an logical switch port number, e.g. 11 for vif11. +# SHA and REPLY_HA are each 12 hex digits. +# SPA and TPA are each 8 hex digits. +test_arp() { + local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5 + local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa} + hv=`vif_to_hv $inport` + as $hv ovs-appctl netdev-dummy/receive $inport $request + + if test X$reply_ha = X; then + # Expect to receive the broadcast ARP on the other logical switch ports + # if no reply is expected. + local i j + for i in 1 2 3; do + for j in 1 2 3; do + if test $i$j != $inport; then + echo $request >> $i$j.expected + fi + done + done + else + # Expect to receive the reply, if any. + local reply=${sha}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa} + local reply_vid=${sha}${reply_ha}810003e808060001080006040002${reply_ha}${tpa}${sha}${spa} + echo $reply_vid >> ${inport}_vid.expected + echo $reply >> $inport.expected + fi +} + +sip=`ip_to_hex 172 31 0 10` +tip=`ip_to_hex 172 31 0 1` + +test_arp vif-north f0f000000011 $sip $tip +# Confirm that vif-north does not get ARP reply +AT_CHECK([wc -l hv4/vif-north-tx.pcap | awk '{print $1}'], [0], [[0 +]]) + +# Set a hypervisor as gateway chassis, for router port 172.31.0.1 +ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv3 +ovn-nbctl --wait=sb sync +sleep 2 + +test_arp vif-north f0f000000011 $sip $tip 000001010207 + +sleep 1 + +# Confirm that vif-north gets a single ARP reply this time +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap], [vif-north.expected]) + +# Confirm that only redirect chassis allowed arp resolution. +OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv3/br-phys_n1-tx.pcap], [vif-north_vid.expected]) +sed -i '/ffffffffffff/d' hv3/br-phys_n1-tx.packets +AT_CHECK([grep 000001010207 hv3/br-phys_n1-tx.packets | wc -l], [0], [[1 +]]) + +# Confirm that other OVN chassis did not generate ARP reply. +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap > hv1/br-phys_n1-tx.packets +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap > hv2/br-phys_n1-tx.packets + +AT_CHECK([grep 000001010207 hv1/br-phys_n1-tx.packets | wc -l], [0], [[0 +]]) +AT_CHECK([grep 000001010207 hv2/br-phys_n1-tx.packets | wc -l], [0], [[0 +]]) + +echo "----------- Post Traffic hv1 dump -----------" +as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int +as hv1 ovs-appctl fdb/show br-phys + +echo "----------- Post Traffic hv2 dump -----------" +as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int +as hv2 ovs-appctl fdb/show br-phys + +echo "----------- Post Traffic hv3 dump -----------" +as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int +as hv3 ovs-appctl fdb/show br-phys + +echo "----------- Post Traffic hv4 dump -----------" +as hv4 ovs-ofctl -O OpenFlow13 dump-flows br-int +as hv4 ovs-appctl fdb/show br-phys + +OVN_CLEANUP([hv1],[hv2],[hv3],[hv4]) + +AT_CLEANUP